1 Data

lung = readRDS("./Data/lung_cancercells_withTP_onlyPatients.rds")
lung_patients = lung$patient.ident %>% unique() %>% as.character()
lung_patients_filtered = lung_patients[!(lung_patients %in% c("X1055new","X1099"))] # remove patients with less than 100 malignant cells
lung = subset(x = lung,subset = patient.ident %in% lung_patients_filtered)
suffix ="xeno_genes_0-5sigma_2-7theta_100iter_26_9"
from cnmf import cNMF
suffix = r.suffix
import pickle
f = open('./Data/cnmf/cnmf_objects/patients_' + suffix + '_cnmf_obj.pckl', 'rb')
cnmf_obj = pickle.load(f)
f.close()

2 Functions

library(stringi)
library(reticulate)
source_from_github(repositoy = "DEG_functions",version = "0.2.47")
source_from_github(repositoy = "cNMF_functions",version = "0.4.01",script_name = "cnmf_functions_V3.R")
source_from_github(repositoy = "sc_general_functions",version = "0.1.28",script_name = "functions.R")
genesets <- msigdb_download("Homo sapiens",category="H") %>% append( msigdb_download("Homo sapiens",category="C2",subcategory = "CP:KEGG"))
genesets[["HIF_targets"]] = hif_targets

genesets_go <- msigdb_download("Homo sapiens",category="C5",subcategory = "GO:BP")

3 K selection plot

plot_path = paste0("/sci/labs/yotamd/lab_share/avishai.wizel/R_projects/EGFR/Data/cnmf/cNMF_patients_Varnorm_Harmony_",suffix,"/cNMF_patients_Varnorm_Harmony_",suffix,".k_selection.png")
knitr::include_graphics(plot_path)

cnmf_obj.consensus(k=3, density_threshold=0.1,show_clustering=True)
cnmf_obj.consensus(k=6, density_threshold=0.1,show_clustering=True)
cnmf_obj.consensus(k=7, density_threshold=0.1,show_clustering=True)
cnmf_obj.consensus(k=8, density_threshold=0.1,show_clustering=True)

4 gep scores for all NMF k’s

density_threshold = 0.1
usage_norm3, gep_scores3, gep_tpm3, topgenes = cnmf_obj.load_results(K=3, density_threshold=density_threshold)
usage_norm6, gep_scores6, gep_tpm6, topgenes = cnmf_obj.load_results(K=6, density_threshold=density_threshold)
usage_norm7, gep_scores7, gep_tpm7, topgenes = cnmf_obj.load_results(K=7, density_threshold=density_threshold)
usage_norm8, gep_scores8, gep_tpm8, topgenes = cnmf_obj.load_results(K=8, density_threshold=density_threshold)

5 Enrichment analysis by top 200 genes of each program

gep_scores3 = py$gep_scores3
gep_scores6 = py$gep_scores6
gep_scores7 = py$gep_scores7
gep_scores8 = py$gep_scores8
reticulate::repl_python()
path = "/sci/labs/yotamd/lab_share/avishai.wizel/R_projects/EGFR/Data/cnmf/cNMF_patients_Varnorm_Harmony_xeno_genes_0-5sigma_2-7theta_100iter_26_9/cNMF_patients_Varnorm_Harmony_xeno_genes_0-5sigma_2-7theta_100iter_26_9.spectra.k_8.dt_0_1.consensus.txt"
spectra_scores = pd.read_csv(path, sep='\t', index_col=0).T
NameError: name 'pd' is not defined
spectra_scores = py$spectra_scores

6 GSEA for every program

  for (col in seq_along(gep_tpm8)) {
     ranked_vec = gep_tpm8[,col] %>% setNames(rownames(gep_tpm8)) %>% sort(decreasing = TRUE) 
     hyp_obj <- hypeR_fgsea(ranked_vec, genesets)
     print_tab(hyp_dots(hyp_obj,title = paste("program",col))+ aes(size=nes),title = paste0("gep",col))
  }

Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj$genesets, : There were 1 pathways for which P-values were not calculated properly due to unbalanced (positive and negative) gene-level statistic values. For such pathways pval, padj, NES, log2err are set to NA. You can try to increase the value of the argument nPermSimple (for example set it nPermSimple = 10000) ## gep1 {.unnumbered }
Warning in grSoftVersion() : unable to load shared object ‘/usr/local/lib/R/modules//R_X11.so’: libXt.so.6: cannot open shared object file: No such file or directory

Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj$genesets, : There were 3 pathways for which P-values were not calculated properly due to unbalanced (positive and negative) gene-level statistic values. For such pathways pval, padj, NES, log2err are set to NA. You can try to increase the value of the argument nPermSimple (for example set it nPermSimple = 10000) ## gep2 {.unnumbered }

Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj$genesets, : There were 2 pathways for which P-values were not calculated properly due to unbalanced (positive and negative) gene-level statistic values. For such pathways pval, padj, NES, log2err are set to NA. You can try to increase the value of the argument nPermSimple (for example set it nPermSimple = 10000) ## gep3 {.unnumbered }

Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj\(genesets, : There were 3 pathways for which P-values were not calculated properly due to unbalanced (positive and negative) gene-level statistic values. For such pathways pval, padj, NES, log2err are set to NA. You can try to increase the value of the argument nPermSimple (for example set it nPermSimple = 10000) Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj\)genesets, : For some pathways, in reality P-values are less than 1e-50. You can set the eps argument to zero for better estimation. ## gep4 {.unnumbered }

Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj$genesets, : There were 3 pathways for which P-values were not calculated properly due to unbalanced (positive and negative) gene-level statistic values. For such pathways pval, padj, NES, log2err are set to NA. You can try to increase the value of the argument nPermSimple (for example set it nPermSimple = 10000) ## gep5 {.unnumbered }

Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj\(genesets, : There were 3 pathways for which P-values were not calculated properly due to unbalanced (positive and negative) gene-level statistic values. For such pathways pval, padj, NES, log2err are set to NA. You can try to increase the value of the argument nPermSimple (for example set it nPermSimple = 10000) Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj\)genesets, : For some pathways, in reality P-values are less than 1e-50. You can set the eps argument to zero for better estimation. ## gep6 {.unnumbered }

Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj\(genesets, : For some of the pathways the P-values were likely overestimated. For such pathways log2err is set to NA. Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj\)genesets, : For some pathways, in reality P-values are less than 1e-50. You can set the eps argument to zero for better estimation. ## gep7 {.unnumbered }

Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj\(genesets, : For some of the pathways the P-values were likely overestimated. For such pathways log2err is set to NA. Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj\)genesets, : For some pathways, in reality P-values are less than 1e-50. You can set the eps argument to zero for better estimation. ## gep8 {.unnumbered }

NA

  for (col in seq_along(gep_scores8)) {
     ranked_vec = gep_scores8[,col] %>% setNames(rownames(gep_scores8)) %>% sort(decreasing = TRUE) 
     hyp_obj <- hypeR_fgsea(ranked_vec, genesets)
     print_tab(hyp_dots(hyp_obj,title = paste("program",col))+ aes(size=nes),title = paste0("gep",col))
  }

gep1

NA

hyp_dots(hyp_obj,title = paste("program",col))

$up

$dn

programs_main_pathways = list(gep1 = 1:2, gep2 = 1:3,gep3 = 1:4)
gep_scores = py$gep_scores8
usage_norm = py$usage_norm8

7 progrmas from NMF

colnames(usage_norm) = paste0("gep",1:8)
#add each metagene to metadata
for (i  in 1:ncol(usage_norm )) {
  metagene_metadata = usage_norm [,i,drop=F]
  lung = AddMetaData(object = lung,metadata = metagene_metadata,col.name = colnames(usage_norm)[i])
}

FeaturePlot(object = lung,features = colnames(usage_norm),ncol = 3)

8 progrmas from NMF

metagenes_mean_compare(dataset = lung,time.point_var = "time.point",prefix = "patient",patient.ident_var = "patient.ident",pre_on = c("pre-treatment","on-treatment"),test = "wilcox.test",programs = colnames(usage_norm)[1:5],without_split = F)

gep1 per patient

gep2 per patient

gep3 per patient

gep4 per patient

gep5 per patient

NA

9 Calculate usage by counts before Harmony

# get expression with genes in cnmf input
genes = rownames(gep_scores)
lung_expression = t(as.matrix(GetAssayData(lung,slot='data'))) 
lung_expression = 2**lung_expression #convert from log2(tpm+1) to tpm
lung_expression = lung_expression-1
lung_expression = lung_expression[,genes] %>% as.data.frame()

all_0_genes = colnames(lung_expression)[colSums(lung_expression==0, na.rm=TRUE)==nrow(lung_expression)] #delete rows that have all 0
genes = genes[!genes %in% all_0_genes]
lung_expression = lung_expression[,!colnames(lung_expression) %in% all_0_genes]
gc(verbose = F)
         used    (Mb) gc trigger    (Mb)   max used    (Mb)

Ncells 13005635 694.6 21808952 1164.8 21808952 1164.8 Vcells 1505803258 11488.4 2197729618 16767.4 1767994410 13488.8


lung_expression = r.lung_expression
genes = r.genes
usage_by_calc = get_usage_from_score(counts=lung_expression,tpm=lung_expression,genes=genes,cnmf_obj=cnmf_obj,k=8,sumTo1=True)
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/bin/python3.7:7: FutureWarning: X.dtype being converted to np.float32 from float64. In the next version of anndata (0.9) conversion will not be automatic. Pass dtype explicitly to avoid this warning. Pass `AnnData(X, dtype=X.dtype, ...)` to get the future behavour.
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/bin/python3.7:8: FutureWarning: X.dtype being converted to np.float32 from float64. In the next version of anndata (0.9) conversion will not be automatic. Pass dtype explicitly to avoid this warning. Pass `AnnData(X, dtype=X.dtype, ...)` to get the future behavour.
usage_by_calc_unnorm = get_usage_from_score(counts=lung_expression,tpm=lung_expression,genes=genes,cnmf_obj=cnmf_obj,k=8,sumTo1=False)
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/bin/python3.7:7: FutureWarning: X.dtype being converted to np.float32 from float64. In the next version of anndata (0.9) conversion will not be automatic. Pass dtype explicitly to avoid this warning. Pass `AnnData(X, dtype=X.dtype, ...)` to get the future behavour.
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/bin/python3.7:8: FutureWarning: X.dtype being converted to np.float32 from float64. In the next version of anndata (0.9) conversion will not be automatic. Pass dtype explicitly to avoid this warning. Pass `AnnData(X, dtype=X.dtype, ...)` to get the future behavour.
usage_by_calc = py$usage_by_calc
usage_by_calc_unnorm =py$usage_by_calc_unnorm
colnames(usage_by_calc) = c("autoimmune","TNFa.NFkB", "hypoxia","hypoxia2", "cell_cycle1", "cell_cycle2","INFg","unknown")
# colnames(usage_by_calc) = paste0("gep",1:8)
#add each metagene to metadata
for (i  in 1:ncol(usage_by_calc )) {
  metagene_metadata = usage_by_calc [,i,drop=F]
  lung = AddMetaData(object = lung,metadata = metagene_metadata,col.name = colnames(usage_by_calc)[i])
}

FeaturePlot(object = lung,features = colnames(usage_by_calc),ncol = 3)

cor_res = cor(gep_scores)
breaks <- c(seq(-1,1,by=0.01))
colors_for_plot <- colorRampPalette(colors = c("blue", "white", "red"))(n = length(breaks))

pht = pheatmap(cor_res,color = colors_for_plot,breaks = breaks)
print_tab(pht,title = "correlation")
groups_list = c(5,6)
patients_geps = union_programs(groups_list = groups_list,all_metagenes = patients_geps)

10 Regulation

metagenes_mean_compare(dataset = lung,time.point_var = "time.point",prefix = "patient",patient.ident_var = "patient.ident",pre_on = c("pre-treatment","on-treatment"),test = "wilcox.test",programs = colnames(usage_by_calc)[1:5],without_split = F)

gep1 per patient

gep2 per patient

gep3 per patient

gep4 per patient

gep5 per patient

NA

11 programs LE genes


programs_main_pathways_names = list()
  for (col in seq_along(gep_tpm8)) {
     ranked_vec = gep_tpm8[,col] %>% setNames(rownames(gep_tpm8)) %>% sort(decreasing = TRUE) 
     hyp_obj <- hypeR_fgsea(ranked_vec, genesets)
     programs_main_pathways_names[[col]] =  hyp_obj$data[programs_main_pathways[[col]],"label",drop=T]
     for (pathway_num in programs_main_pathways[[col]]) {
        le_genes =  hyp_obj$data[pathway_num,,drop=F] %>% pull("le") %>% strsplit(",") %>% unlist()
        score = FetchData(object = lung,vars = le_genes) %>% rowMeans()
        pathway_name = paste0(hyp_obj$data[pathway_num,"label",drop=F],"_le")
        lung=AddMetaData(lung,score,col.name = pathway_name)
        print_tab(FeaturePlot(object = lung,features = pathway_name),title = pathway_name)
     }
  }

KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION_le

KEGG_SYSTEMIC_LUPUS_ERYTHEMATOSUS_le

Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj\(genesets, : There were 1 pathways for which P-values were not calculated properly due to unbalanced (positive and negative) gene-level statistic values. For such pathways pval, padj, NES, log2err are set to NA. You can try to increase the value of the argument nPermSimple (for example set it nPermSimple = 10000) Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj\)genesets, : For some pathways, in reality P-values are less than 1e-50. You can set the eps argument to zero for better estimation. ## KEGG_CELL_ADHESION_MOLECULES_CAMS_le {.unnumbered }

HALLMARK_PROTEIN_SECRETION_le

KEGG_LEUKOCYTE_TRANSENDOTHELIAL_MIGRATION_le

Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj$genesets, : There were 1 pathways for which P-values were not calculated properly due to unbalanced (positive and negative) gene-level statistic values. For such pathways pval, padj, NES, log2err are set to NA. You can try to increase the value of the argument nPermSimple (for example set it nPermSimple = 10000) ## HALLMARK_GLYCOLYSIS_le {.unnumbered }

HALLMARK_MTORC1_SIGNALING_le

HALLMARK_HYPOXIA_le

HALLMARK_INFLAMMATORY_RESPONSE_le

Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj\(genesets, : There were 3 pathways for which P-values were not calculated properly due to unbalanced (positive and negative) gene-level statistic values. For such pathways pval, padj, NES, log2err are set to NA. You can try to increase the value of the argument nPermSimple (for example set it nPermSimple = 10000) Warning in fgsea::fgseaMultilevel(stats = signature, pathways = gsets.obj\)genesets, : For some pathways, in reality P-values are less than 1e-50. You can set the eps argument to zero for better estimation. Error in programs_main_pathways[[col]] : subscript out of bounds

programs_main_pathways_names = list()
  for (col in seq_along(gep_scores)) {
     ranked_vec = gep_scores[,col] %>% setNames(rownames(gep_scores)) %>% sort(decreasing = TRUE) 
     hyp_obj <- hypeR_fgsea(ranked_vec, genesets)
     programs_main_pathways_names[[col]] =  hyp_obj$data[programs_main_pathways[[col]],"label",drop=T]
     for (pathway_num in programs_main_pathways[[col]]) {
        le_genes =  hyp_obj$data[pathway_num,,drop=F] %>% pull("le") %>% strsplit(",") %>% unlist()
        score = FetchData(object = lung,vars = le_genes) %>% rowMeans()
        pathway_name = paste0(hyp_obj$data[pathway_num,"label",drop=F],"_le")
        lung=AddMetaData(lung,score,col.name = pathway_name)
        print_tab(FeaturePlot(object = lung,features = pathway_name),title = pathway_name)
     }
  }

KEGG_AUTOIMMUNE_THYROID_DISEASE_le

KEGG_TYPE_I_DIABETES_MELLITUS_le

KEGG_INTESTINAL_IMMUNE_NETWORK_FOR_IGA_PRODUCTION_le

KEGG_SYSTEMIC_LUPUS_ERYTHEMATOSUS_le

KEGG_ALLOGRAFT_REJECTION_le

HALLMARK_TNFA_SIGNALING_VIA_NFKB_le

HALLMARK_P53_PATHWAY_le

HALLMARK_UV_RESPONSE_DN_le

KEGG_TGF_BETA_SIGNALING_PATHWAY_le

HALLMARK_HYPOXIA_le

HALLMARK_GLYCOLYSIS_le

HALLMARK_EPITHELIAL_MESENCHYMAL_TRANSITION_le

Error in programs_main_pathways[[col]] : subscript out of bounds

genes = gep_scores[,2] %>% setNames(rownames(gep_scores)) %>% sort(decreasing = TRUE) %>% names
score = FetchData(object = lung,vars = genes[1:100]) %>% rowMeans()
pathway_name = "top_100_nfkb"
lung=AddMetaData(lung,score,col.name = pathway_name)
metagenes_mean_compare(dataset = lung,time.point_var = "time.point",prefix = "patient",patient.ident_var = "patient.ident",pre_on = c("pre-treatment","on-treatment"),test = "wilcox.test",programs = pathway_name,without_split = F)

HIF_targets_le per patient

NA

12 programs LE genes regulation

metagenes_mean_compare(dataset = lung,time.point_var = "time.point",prefix = "patient",patient.ident_var = "patient.ident",pre_on = c("pre-treatment","on-treatment"),test = "wilcox.test",programs = programs_main_pathways_names %>% unlist() %>% paste0("_le"),without_split = F)

KEGG_AUTOIMMUNE_THYROID_DISEASE_le per patient

KEGG_TYPE_I_DIABETES_MELLITUS_le per patient

KEGG_INTESTINAL_IMMUNE_NETWORK_FOR_IGA_PRODUCTION_le per patient

KEGG_SYSTEMIC_LUPUS_ERYTHEMATOSUS_le per patient

KEGG_ALLOGRAFT_REJECTION_le per patient

HALLMARK_TNFA_SIGNALING_VIA_NFKB_le per patient

HALLMARK_P53_PATHWAY_le per patient

HALLMARK_UV_RESPONSE_DN_le per patient

KEGG_TGF_BETA_SIGNALING_PATHWAY_le per patient

HALLMARK_HYPOXIA_le per patient

HALLMARK_GLYCOLYSIS_le per patient

HALLMARK_EPITHELIAL_MESENCHYMAL_TRANSITION_le per patient

NA

13 Top program 2 genes expression correlation

annotation = plot_genes_cor(dataset = xeno,geneIds = top_ot,height = 2.8)
##   genes expression heatmap {.unnumbered }  

NA

14 cluster program expression

for (chosen_clusters in 1:length(unique(annotation$cluster))) {
  chosen_genes = annotation %>% dplyr::filter(cluster == chosen_clusters) %>% rownames() #take relevant genes
  # print(chosen_genes)
  hyp_obj <- hypeR(chosen_genes, genesets, test = "hypergeometric", fdr=1, plotting=F,background = rownames(patients_geps))

   scoresAndIndices <- getPathwayScores(lung@assays$RNA@data, chosen_genes)
  lung=AddMetaData(lung,scoresAndIndices$pathwayScores,paste0("cluster",chosen_clusters))

  
  print_tab(plt = 
              hyp_dots(hyp_obj,size_by = "none",title = paste0("cluster",chosen_clusters))+
              FeaturePlot(object = lung,features = paste0("cluster",chosen_clusters)),
            title = chosen_clusters)
  
  cor_res = cor(lung$interferon_like,lung[[paste0("cluster",chosen_clusters)]])
# print(paste("correlation of TNFa program to", paste0("cluster",chosen_clusters),":", cor_res))

}

1

2

3

4

5

6

7

NA

15 cluster program regulation

metagenes_mean_compare(dataset = lung,time.point_var = "time.point",prefix = "patient",patient.ident_var = "patient.ident",pre_on = c("pre-treatment","on-treatment"),test = "wilcox.test",programs = "CFLAR",without_split = F)

CFLAR per patient

NA

metagenes_mean_compare(dataset = lung,time.point_var = "time.point",prefix = "patient",patient.ident_var = "patient.ident",pre_on = c("pre-treatment","on-treatment"),test = "wilcox.test",programs = paste0("cluster",1:length(unique(annotation$cluster))),without_split = F)

cluster1 per patient

cluster2 per patient

cluster3 per patient

cluster4 per patient

cluster5 per patient

cluster6 per patient

cluster7 per patient

NA

16 cluster program enrichment

top_ot = patients_geps[order(patients_geps[,1],decreasing = T),2,drop = F]%>% head(10) %>% rownames()

chosen_genes = top_ot
 scoresAndIndices <- getPathwayScores(lung@assays$RNA@data, chosen_genes)
  lung=AddMetaData(lung,scoresAndIndices$pathwayScores,"immune_gep")

  
  
  cor_res = cor(lung$interferon_like,lung[["immune_gep"]])
print(paste("correlation of TNFa program to", "immune_gep",":", cor_res))
for (chosen_clusters in 1:length(unique(annotation$cluster))) {
  chosen_genes = annotation %>% dplyr::filter(cluster == chosen_clusters) %>% rownames() #take relevant genes
  # print(chosen_genes)
  hyp_obj <- hypeR(chosen_genes, genesets, test = "hypergeometric", fdr=1, plotting=F,background = rownames(patients_geps))

   scoresAndIndices <- getPathwayScores(lung@assays$RNA@data, chosen_genes)
  lung=AddMetaData(lung,scoresAndIndices$pathwayScores,paste0("cluster",chosen_clusters))

  
  print_tab(plt = 
              hyp_dots(hyp_obj,size_by = "none",title = paste0("cluster",chosen_clusters))+
              FeaturePlot(object = lung,features = paste0("cluster",chosen_clusters)),
            title = chosen_clusters)
  
  cor_res = cor(lung$interferon_like,lung[[paste0("cluster",chosen_clusters)]])
print(paste("correlation of TNFa program to", paste0("cluster",chosen_clusters),":", cor_res))

}
##   1 {.unnumbered }  

 

[1] "correlation of TNFa program to cluster1 : 0.155039646876199"
##   2 {.unnumbered }  

 

[1] "correlation of TNFa program to cluster2 : 0.35203084026905"
##   3 {.unnumbered }  

 

[1] "correlation of TNFa program to cluster3 : 0.163348283357048"
##   4 {.unnumbered }  

 

[1] "correlation of TNFa program to cluster4 : -0.0422938672700064"
##   5 {.unnumbered }  

 

[1] "correlation of TNFa program to cluster5 : 0.430856560234176"
##   6 {.unnumbered }  

 

[1] "correlation of TNFa program to cluster6 : -0.107526561142319"
##   7 {.unnumbered }  

 

[1] "correlation of TNFa program to cluster7 : -0.0617622603084714"
import anndata as ad
import scanpy as sc
import numpy as np
from sklearn.decomposition import non_negative_factorization
import pandas as pd


lung_expression = r.lung_expression
genes = r.genes
counts=lung_expression
tpm=lung_expression

counts_adata = ad.AnnData(counts)
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/bin/python3.7:1: FutureWarning: X.dtype being converted to np.float32 from float64. In the next version of anndata (0.9) conversion will not be automatic. Pass dtype explicitly to avoid this warning. Pass `AnnData(X, dtype=X.dtype, ...)` to get the future behavour.
tpm_adata = ad.AnnData(tpm)
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/bin/python3.7:1: FutureWarning: X.dtype being converted to np.float32 from float64. In the next version of anndata (0.9) conversion will not be automatic. Pass dtype explicitly to avoid this warning. Pass `AnnData(X, dtype=X.dtype, ...)` to get the future behavour.
#get matrices
norm_counts = get_norm_counts(counts=counts_adata,tpm=tpm_adata,high_variance_genes_filter=np.array(genes)) #norm counts like cnmf
norm_counts = norm_counts.to_df()
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/lib/python3.7/site-packages/pandas/core/computation/expressions.py:69: VisibleDeprecationWarning: Creating an ndarray from nested sequences exceeding the maximum number of dimensions of 32 is deprecated. If you mean to do this, you must specify 'dtype=object' when creating the ndarray.
  return op(a, b)
norm_counts = py$norm_counts %>% as.data.frame()
df = gep_scores8
top = 200
program_name =paste0("top_", top, "_genes")
expression = lung_expression
genes = df[,2] %>% setNames(rownames(df)) %>% sort(decreasing = TRUE) %>% names()
le_genes
 [1] "ZFP36"   "JUNB"    "CEBPD"   "FOS"     "FOSB"    "IER2"    "EGR1"    "JUN"     "NR4A1"   "RHOB"    "GADD45B" "CXCL2"   "BTG2"    "ATF3"    "SAT1"   
[16] "BCL6"    "NR4A2"   "NR4A3"   "SOCS3"   "KLF2"    "REL"     "EGR2"    "NFKBIA"  "PDE4B"   "IL6"     "IRF1"    "ETS2"   
# mult = as.matrix(t(df[genes[1:top],2,drop=F])) %*% (lung@assays$RNA@data[genes[1:top],] %>% as.matrix()); mult = mult[1,]
mult =  (expression[,genes[1:top]] %>% as.matrix()) %*%  as.matrix((df[genes[1:top],2,drop=F]))
# mult =  (expression[,le_genes] %>% as.matrix()) %*%  as.matrix((df[le_genes,2,drop=F]))

cor_res = cor(usage_by_calc[,2],mult)
print(paste("correlation of TNFa/NFkB program to", program_name,":", cor_res))
[1] "correlation of TNFa/NFkB program to top_200_genes : 0.72144922898689"
df = gep_scores8
expression  = lung_expression
top = 200
program_name =paste0("top_", top, "_genes")
prognam_index= 2
top_genes = df[,prognam_index] %>% setNames(rownames(df)) %>% sort(decreasing = TRUE) %>% names()

mult =  (expression[,top_genes[1:top]] %>% as.matrix())  %*% as.matrix(df[top_genes[1:top],prognam_index,drop=F])

avg_tpm = expression[,top_genes[1:top]] %>% rowMeans()
avg_tpm_all_genes = lung@assays$RNA@data[top_genes[1:top],] %>% 2^. %>% magrittr::subtract(1) %>%  colMeans()

cor(usage_by_calc[,prognam_index],mult)
             2
[1,] 0.7214492
cor(usage_by_calc[,prognam_index],avg_tpm)
[1] 0.6604709
cor(usage_by_calc[,prognam_index],avg_tpm_all_genes)
[1] 0.6604709
metagenes_mean_compare(dataset = lung,time.point_var = "time.point",prefix = "patient",patient.ident_var = "patient.ident",pre_on = c("pre-treatment","on-treatment"),test = "wilcox.test",programs = "immune_gep_mult",without_split = F)
##   immune_gep_mult per patient {.unnumbered }  

NA
# get expression with genes in cnmf input
lung = FindVariableFeatures(object = lung,nfeatures = 2000)

Calculating gene variances 0% 10 20 30 40 50 60 70 80 90 100% [—-|—-|—-|—-|—-|—-|—-|—-|—-|—-| **************************************************| Calculating feature variances of standardized and clipped values 0% 10 20 30 40 50 60 70 80 90 100% [—-|—-|—-|—-|—-|—-|—-|—-|—-|—-| **************************************************|

genes = rownames(lung)[rownames(lung) %in% VariableFeatures(object = xeno)[1:2000]]

lung_expression = t(as.matrix(GetAssayData(lung,slot='data'))) 
lung_expression = 2**lung_expression #convert from log2(tpm+1) to tpm
lung_expression = lung_expression-1
lung_expression = lung_expression[,genes] %>% as.data.frame()

all_0_genes = colnames(lung_expression)[colSums(lung_expression==0, na.rm=TRUE)==nrow(lung_expression)] #delete rows that have all 0
genes = genes[!genes %in% all_0_genes]
lung_expression = lung_expression[,!colnames(lung_expression) %in% all_0_genes]
gc(verbose = F)
         used    (Mb) gc trigger    (Mb)   max used    (Mb)

Ncells 12735363 680.2 21780954 1163.3 21780954 1163.3 Vcells 1494422492 11401.6 2197586153 16766.3 2197586058 16766.3


lung_expression = r.lung_expression
genes = r.genes
# gep_scores = r.patients_geps
usage_by_calc = get_usage_from_score(counts=lung_expression,tpm=lung_expression,genes=genes,cnmf_obj=cnmf_obj,k=8,sumTo1=False)
def get_usage_from_score(counts,tpm, genes,cnmf_obj,k, sumTo1 = True,do_norm_counts = True): #based on 'consensus' method
      import anndata as ad
      import scanpy as sc
      import numpy as np
      from sklearn.decomposition import non_negative_factorization
      import pandas as pd
      counts_adata = ad.AnnData(counts)
      tpm_adata = ad.AnnData(tpm)
      
      #get matrices
      if(do_norm_counts):
        norm_counts = get_norm_counts(counts=counts_adata,tpm=tpm_adata,high_variance_genes_filter=np.array(genes)) #norm counts like cnmf
      else:
        norm_counts = ad.AnnData(counts)
      spectra_original = cnmf_obj.get_median_spectra(k=k) #get score 
      
      # filter 
      spectra = spectra_original[spectra_original.columns.intersection(genes)] #remove genes not in @genes
      norm_counts = norm_counts[:, spectra.columns].copy() #remove genes not in spectra
      spectra = spectra.T.reindex(norm_counts.to_df().columns).T #reorder spectra genes like norm_counts
      
      # calculate usage
      usage_by_calc,_,_ = non_negative_factorization(X=norm_counts.X, H = spectra.values, update_H=False,n_components = k,max_iter=1000,init ='random')
      usage_by_calc = pd.DataFrame(usage_by_calc, index=counts.index, columns=spectra.index) #insert to df+add names
      
      #normalize
      if(sumTo1):
          usage_by_calc = usage_by_calc.div(usage_by_calc.sum(axis=1), axis=0) # sum rows to 1 and assign to main df
      
      # reorder
        # get original order
      original_norm_counts = sc.read(cnmf_obj.paths['normalized_counts'])
      usage_by_calc_original,_,_ = non_negative_factorization(X=original_norm_counts.X, H = spectra_original.values, update_H=False,n_components = k,max_iter=1000,init ='random')
      usage_by_calc_original = pd.DataFrame(usage_by_calc_original, index=original_norm_counts.obs.index, columns=spectra_original.index)  
      norm_original_usages =usage_by_calc_original.div(usage_by_calc_original.sum(axis=1), axis=0)      
      reorder = norm_original_usages.sum(axis=0).sort_values(ascending=False)
        #apply
      usage_by_calc = usage_by_calc.loc[:, reorder.index]
      return(usage_by_calc)
usage_by_calc = get_usage_from_score(counts=lung_expression,tpm=lung_expression,genes=genes,cnmf_obj=cnmf_obj,k=8,sumTo1=True,do_norm_counts=False)
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/bin/python3.7:7: FutureWarning: X.dtype being converted to np.float32 from float64. In the next version of anndata (0.9) conversion will not be automatic. Pass dtype explicitly to avoid this warning. Pass `AnnData(X, dtype=X.dtype, ...)` to get the future behavour.
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/bin/python3.7:8: FutureWarning: X.dtype being converted to np.float32 from float64. In the next version of anndata (0.9) conversion will not be automatic. Pass dtype explicitly to avoid this warning. Pass `AnnData(X, dtype=X.dtype, ...)` to get the future behavour.
/sci/labs/yotamd/lab_share/avishai.wizel/python_envs/miniconda/envs/cnmf_env_6/bin/python3.7:14: FutureWarning: X.dtype being converted to np.float32 from float64. In the next version of anndata (0.9) conversion will not be automatic. Pass dtype explicitly to avoid this warning. Pass `AnnData(X, dtype=X.dtype, ...)` to get the future behavour.
usage_by_calc = py$usage_by_calc
groups_list = c(4,3,6)
usage_by_calc = union_programs(groups_list = groups_list,all_metagenes = usage_by_calc)
# usage_by_calc = apply(usage_by_calc, MARGIN = 1, sum_2_one) %>% t() %>% as.data.frame()
usage_by_calc =usage_by_calc %>% rename(cell_cycle = gep4.3.6, hypoxia_like = gep2, interferon_like = gep1, TNFa =  gep5, INF2 = gep7)
lung=AddMetaData(lung,usage_by_calc[,1,drop=F],"immune_gep_no_sum2one")
metagenes_mean_compare(dataset = lung,time.point_var = "time.point",prefix = "patient",patient.ident_var = "patient.ident",pre_on = c("pre-treatment","on-treatment"),test = "wilcox.test",programs = "immune_gep_no_sum2one",without_split = F)

17 Program 2

col=1
ranked_vec = gep_scores8[, col] %>% setNames(rownames(gep_scores8)) %>% sort(decreasing = TRUE)
print (paste("running gep",col))
hyp_obj <-hypeR_fgsea(ranked_vec, genesets_go, up_only = T)

print(hyp_dots(hyp_obj, title = paste("program", col), abrv = 70) + aes(size =nes))
  
col=2
ranked_vec = gep_scores8[, col] %>% setNames(rownames(gep_scores8)) %>% sort(decreasing = TRUE)
print (paste("running gep",col))
hyp_obj <-hypeR_fgsea(ranked_vec, genesets_go, up_only = T)

print(hyp_dots(hyp_obj, title = paste("program", col), abrv = 70) + aes(size =nes))
  
pathway_num = 1
le_genes = hyp_obj$as.data.frame()[pathway_num,"le"] %>% strsplit(",") %>% unlist()
score = (lung_expression[, le_genes] %>% as.matrix())  %*% as.matrix(gep_scores8[le_genes, 2, drop =F])

# score = FetchData(object = lung,vars = le_genes) %>% rowMeans()
pathway_name = paste0(hyp_obj$data[pathway_num, "label", drop = F], "_le") %>% gsub(pattern = " ",replacement = "_")
lung = AddMetaData(lung, score, col.name = pathway_name)
print_tab(FeaturePlot(object = lung,features = pathway_name),title = pathway_name)
pathway_num = 1
le_genes = hyp_obj$as.data.frame()[pathway_num,"le"] %>% strsplit(",") %>% unlist()
score = (lung_expression[, le_genes[ le_genes %in% top_genes[1:200]]] %>% as.matrix())  %*% as.matrix(gep_scores8[le_genes[ le_genes %in% top_genes[1:200]], 2, drop =F])
cor(score,usage_by_calc[,2])
score = FetchData(object = lung,vars = le_genes[ le_genes %in% top_genes[1:200]]) %>% rowMeans()
cor(score,usage_by_calc[,2])

        
top_genes = gep_scores8[,2] %>% setNames(rownames(gep_scores8)) %>% sort(decreasing = TRUE) %>% names()
score = (lung_expression[, top_genes[1:200]] %>% as.matrix())  %*% as.matrix(gep_scores8[top_genes[1:200], 2, drop =F])
 top_genes %in%  le_genes %>% which()
cor(score,usage_by_calc[,2])

pathway_name = paste0(hyp_obj$data[pathway_num, "label", drop = F], "_le") %>% gsub(pattern = " ",replacement = "_")
lung = AddMetaData(lung, score, col.name = pathway_name)
print_tab(FeaturePlot(object = lung,features = pathway_name),title = pathway_name)
metagenes_mean_compare(dataset = lung,time.point_var = "time.point",prefix = "patient",patient.ident_var = "patient.ident",pre_on = c("pre-treatmen
                                                                                                                                      t","on-treatment"),test = "wilcox.test",programs = pathway_name,without_split = F)

18 programs LE genes


programs_main_pathways_names = list()
  for (col in seq_along(gep_scores8)[1:3]) {
     ranked_vec = gep_scores8[,col] %>% setNames(rownames(gep_scores8)) %>% sort(decreasing = TRUE) 
     hyp_obj <- hypeR_fgsea(ranked_vec, genesets)
     programs_main_pathways_names[[col]] =  hyp_obj$data[programs_main_pathways[[col]],"label",drop=T]
     for (pathway_num in programs_main_pathways[[col]]) {
        le_genes =  hyp_obj$data[pathway_num,,drop=F] %>% pull("le") %>% strsplit(",") %>% unlist()
        # score = (lung_expression[,le_genes] %>% as.matrix())  %*% as.matrix(gep_scores8[le_genes,col,drop=F])
        # score = score %>% as.vector()
        score = FetchData(object = lung,vars = le_genes) %>% rowMeans()
        pathway_name = paste0(hyp_obj$data[pathway_num,"label",drop=F],"_le")
        lung=AddMetaData(lung,score,col.name = pathway_name)
        cor_res = cor(score,usage_by_calc[,col])
        print_tab(FeaturePlot(object = lung,features = pathway_name)+ggtitle(pathway_name, subtitle = paste("cor to usage:",cor_res)),title = pathway_name)
     }
  }

19 programs LE genes

metagenes_mean_compare(dataset = lung,time.point_var = "time.point",prefix = "patient",patient.ident_var = "patient.ident",pre_on = c("pre-treatment","on-treatment"),test = "wilcox.test",programs = programs_main_pathways_names %>% unlist() %>% paste0("_le") ,without_split = F)
LS0tCnRpdGxlOiAnYHIgcnN0dWRpb2FwaTo6Z2V0U291cmNlRWRpdG9yQ29udGV4dCgpJHBhdGggJT4lIGJhc2VuYW1lKCkgJT4lIGdzdWIocGF0dGVybiA9ICJcXC5SbWQiLHJlcGxhY2VtZW50ID0gIiIpYCcgCmF1dGhvcjogIkF2aXNoYWkgV2l6ZWwiCmRhdGU6ICdgciBTeXMudGltZSgpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19jb2xsYXBzZTogeWVzCiAgICB0b2NfZmxvYXQ6IAogICAgICBjb2xsYXBzZWQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRvY19kZXB0aDogMQplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCgojIERhdGEKCmBgYHtyfQpsdW5nID0gcmVhZFJEUygiLi9EYXRhL2x1bmdfY2FuY2VyY2VsbHNfd2l0aFRQX29ubHlQYXRpZW50cy5yZHMiKQpsdW5nX3BhdGllbnRzID0gbHVuZyRwYXRpZW50LmlkZW50ICU+JSB1bmlxdWUoKSAlPiUgYXMuY2hhcmFjdGVyKCkKbHVuZ19wYXRpZW50c19maWx0ZXJlZCA9IGx1bmdfcGF0aWVudHNbIShsdW5nX3BhdGllbnRzICVpbiUgYygiWDEwNTVuZXciLCJYMTA5OSIpKV0gIyByZW1vdmUgcGF0aWVudHMgd2l0aCBsZXNzIHRoYW4gMTAwIG1hbGlnbmFudCBjZWxscwpsdW5nID0gc3Vic2V0KHggPSBsdW5nLHN1YnNldCA9IHBhdGllbnQuaWRlbnQgJWluJSBsdW5nX3BhdGllbnRzX2ZpbHRlcmVkKQpgYGAKCgpgYGB7cn0Kc3VmZml4ID0ieGVub19nZW5lc18wLTVzaWdtYV8yLTd0aGV0YV8xMDBpdGVyXzI2XzkiCmBgYAoKCmBgYHtweXRob259CmZyb20gY25tZiBpbXBvcnQgY05NRgpzdWZmaXggPSByLnN1ZmZpeAppbXBvcnQgcGlja2xlCmYgPSBvcGVuKCcuL0RhdGEvY25tZi9jbm1mX29iamVjdHMvcGF0aWVudHNfJyArIHN1ZmZpeCArICdfY25tZl9vYmoucGNrbCcsICdyYicpCmNubWZfb2JqID0gcGlja2xlLmxvYWQoZikKZi5jbG9zZSgpCmBgYAoKCgojIEZ1bmN0aW9ucwoKYGBge3J9CmxpYnJhcnkoc3RyaW5naSkKbGlicmFyeShyZXRpY3VsYXRlKQpzb3VyY2VfZnJvbV9naXRodWIocmVwb3NpdG95ID0gIkRFR19mdW5jdGlvbnMiLHZlcnNpb24gPSAiMC4yLjQ3IikKc291cmNlX2Zyb21fZ2l0aHViKHJlcG9zaXRveSA9ICJjTk1GX2Z1bmN0aW9ucyIsdmVyc2lvbiA9ICIwLjQuMDEiLHNjcmlwdF9uYW1lID0gImNubWZfZnVuY3Rpb25zX1YzLlIiKQpzb3VyY2VfZnJvbV9naXRodWIocmVwb3NpdG95ID0gInNjX2dlbmVyYWxfZnVuY3Rpb25zIix2ZXJzaW9uID0gIjAuMS4yOCIsc2NyaXB0X25hbWUgPSAiZnVuY3Rpb25zLlIiKQpgYGAKCmBgYHtyfQpnZW5lc2V0cyA8LSBtc2lnZGJfZG93bmxvYWQoIkhvbW8gc2FwaWVucyIsY2F0ZWdvcnk9IkgiKSAlPiUgYXBwZW5kKCBtc2lnZGJfZG93bmxvYWQoIkhvbW8gc2FwaWVucyIsY2F0ZWdvcnk9IkMyIixzdWJjYXRlZ29yeSA9ICJDUCIpKQpnZW5lc2V0c1tbIkhJRl90YXJnZXRzIl1dID0gaGlmX3RhcmdldHMKCmdlbmVzZXRzX2dvIDwtIG1zaWdkYl9kb3dubG9hZCgiSG9tbyBzYXBpZW5zIixjYXRlZ29yeT0iQzUiLHN1YmNhdGVnb3J5ID0gIkdPOkJQIikKYGBgCgojIEsgc2VsZWN0aW9uIHBsb3QKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NH0KcGxvdF9wYXRoID0gcGFzdGUwKCIvc2NpL2xhYnMveW90YW1kL2xhYl9zaGFyZS9hdmlzaGFpLndpemVsL1JfcHJvamVjdHMvRUdGUi9EYXRhL2NubWYvY05NRl9wYXRpZW50c19WYXJub3JtX0hhcm1vbnlfIixzdWZmaXgsIi9jTk1GX3BhdGllbnRzX1Zhcm5vcm1fSGFybW9ueV8iLHN1ZmZpeCwiLmtfc2VsZWN0aW9uLnBuZyIpCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKHBsb3RfcGF0aCkKYGBgCgoKYGBge3B5dGhvbn0KY25tZl9vYmouY29uc2Vuc3VzKGs9MywgZGVuc2l0eV90aHJlc2hvbGQ9MC4xLHNob3dfY2x1c3RlcmluZz1UcnVlKQpjbm1mX29iai5jb25zZW5zdXMoaz02LCBkZW5zaXR5X3RocmVzaG9sZD0wLjEsc2hvd19jbHVzdGVyaW5nPVRydWUpCmNubWZfb2JqLmNvbnNlbnN1cyhrPTcsIGRlbnNpdHlfdGhyZXNob2xkPTAuMSxzaG93X2NsdXN0ZXJpbmc9VHJ1ZSkKY25tZl9vYmouY29uc2Vuc3VzKGs9OCwgZGVuc2l0eV90aHJlc2hvbGQ9MC4xLHNob3dfY2x1c3RlcmluZz1UcnVlKQpgYGAKCgojIGdlcCBzY29yZXMgZm9yIGFsbCBOTUYgaydzCmBgYHtweXRob259CmRlbnNpdHlfdGhyZXNob2xkID0gMC4xCnVzYWdlX25vcm0zLCBnZXBfc2NvcmVzMywgZ2VwX3RwbTMsIHRvcGdlbmVzID0gY25tZl9vYmoubG9hZF9yZXN1bHRzKEs9MywgZGVuc2l0eV90aHJlc2hvbGQ9ZGVuc2l0eV90aHJlc2hvbGQpCnVzYWdlX25vcm02LCBnZXBfc2NvcmVzNiwgZ2VwX3RwbTYsIHRvcGdlbmVzID0gY25tZl9vYmoubG9hZF9yZXN1bHRzKEs9NiwgZGVuc2l0eV90aHJlc2hvbGQ9ZGVuc2l0eV90aHJlc2hvbGQpCnVzYWdlX25vcm03LCBnZXBfc2NvcmVzNywgZ2VwX3RwbTcsIHRvcGdlbmVzID0gY25tZl9vYmoubG9hZF9yZXN1bHRzKEs9NywgZGVuc2l0eV90aHJlc2hvbGQ9ZGVuc2l0eV90aHJlc2hvbGQpCnVzYWdlX25vcm04LCBnZXBfc2NvcmVzOCwgZ2VwX3RwbTgsIHRvcGdlbmVzID0gY25tZl9vYmoubG9hZF9yZXN1bHRzKEs9OCwgZGVuc2l0eV90aHJlc2hvbGQ9ZGVuc2l0eV90aHJlc2hvbGQpCgpgYGAKCiMgRW5yaWNobWVudCBhbmFseXNpcyBieSB0b3AgMjAwIGdlbmVzIG9mIGVhY2ggcHJvZ3JhbSB7LnRhYnNldH0KYGBge3IgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9OCwgcmVzdWx0cz0nYXNpcyd9CmdlcF9zY29yZXMzID0gcHkkZ2VwX3Njb3JlczMKZ2VwX3Njb3JlczYgPSBweSRnZXBfc2NvcmVzNgpnZXBfc2NvcmVzNyA9IHB5JGdlcF9zY29yZXM3CmdlcF9zY29yZXM4ID0gcHkkZ2VwX3Njb3JlczgKCmdlcF90cG04ID0gcHkkZ2VwX3RwbTgKdG9wX2dlbmVzID0gcHkkdG9wZ2VuZXMKYGBgCgoKYGBge3B5dGhvbn0KaW1wb3J0IHBhbmRhcyBhcyBwZApzcGVjdHJhX29yaWdpbmFsID0gY25tZl9vYmouZ2V0X21lZGlhbl9zcGVjdHJhKGs9OCkuVCAjZ2V0IHNjb3JlIApwYXRoID0gIi9zY2kvbGFicy95b3RhbWQvbGFiX3NoYXJlL2F2aXNoYWkud2l6ZWwvUl9wcm9qZWN0cy9FR0ZSL0RhdGEvY25tZi9jTk1GX3BhdGllbnRzX1Zhcm5vcm1fSGFybW9ueV94ZW5vX2dlbmVzXzAtNXNpZ21hXzItN3RoZXRhXzEwMGl0ZXJfMjZfOS9jTk1GX3BhdGllbnRzX1Zhcm5vcm1fSGFybW9ueV94ZW5vX2dlbmVzXzAtNXNpZ21hXzItN3RoZXRhXzEwMGl0ZXJfMjZfOS5zcGVjdHJhLmtfOC5kdF8wXzEuY29uc2Vuc3VzLnR4dCIKc3BlY3RyYV9zY29yZXMgPSBwZC5yZWFkX2NzdihwYXRoLCBzZXA9J1x0JywgaW5kZXhfY29sPTApLlQKCmBgYAoKYGBge3J9CnNwZWN0cmFfc2NvcmVzID0gcHkkc3BlY3RyYV9zY29yZXMKYGBgCgojIEdTRUEgZm9yIGV2ZXJ5IHByb2dyYW0gey50YWJzZXR9CgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KICBmb3IgKGNvbCBpbiBzZXFfYWxvbmcoZ2VwX3RwbTgpKSB7CiAgICAgcmFua2VkX3ZlYyA9IGdlcF90cG04Wyxjb2xdICU+JSBzZXROYW1lcyhyb3duYW1lcyhnZXBfdHBtOCkpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUUlVFKSAKICAgICBoeXBfb2JqIDwtIGh5cGVSX2Znc2VhKHJhbmtlZF92ZWMsIGdlbmVzZXRzKQogICAgIHByaW50X3RhYihoeXBfZG90cyhoeXBfb2JqLHRpdGxlID0gcGFzdGUoInByb2dyYW0iLGNvbCkpKyBhZXMoc2l6ZT1uZXMpLHRpdGxlID0gcGFzdGUwKCJnZXAiLGNvbCkpCiAgfQpgYGAKCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQogIGZvciAoY29sIGluIHNlcV9hbG9uZyhnZXBfc2NvcmVzOCkpIHsKICAgICByYW5rZWRfdmVjID0gZ2VwX3Njb3JlczhbLGNvbF0gJT4lIHNldE5hbWVzKHJvd25hbWVzKGdlcF9zY29yZXM4KSkgJT4lIHNvcnQoZGVjcmVhc2luZyA9IFRSVUUpIAogICAgIGh5cF9vYmogPC0gaHlwZVJfZmdzZWEocmFua2VkX3ZlYywgZ2VuZXNldHMpCiAgICAgcHJpbnRfdGFiKGh5cF9kb3RzKGh5cF9vYmosdGl0bGUgPSBwYXN0ZSgicHJvZ3JhbSIsY29sKSkrIGFlcyhzaXplPW5lcyksdGl0bGUgPSBwYXN0ZTAoImdlcCIsY29sKSkKICB9CmBgYAoKYGBge3IgcmVzdWx0cz0nYXNpcyd9CiAgZm9yIChjb2wgaW4gc2VxX2Fsb25nKGdlcF9zY29yZXM4KSkgewogICAgIHJhbmtlZF92ZWMgPSBnZXBfc2NvcmVzOFssY29sXSAlPiUgc2V0TmFtZXMocm93bmFtZXMoZ2VwX3Njb3JlczgpKSAlPiUgc29ydChkZWNyZWFzaW5nID0gVFJVRSkgCiAgICAgaHlwX29iaiA8LSBoeXBlUl9mZ3NlYShyYW5rZWRfdmVjLCBnZW5lc2V0cyx1cF9vbmx5ID0gRikKICAgICBwcmludF90YWIoaHlwX2RvdHMoaHlwX29iaix0aXRsZSA9IHBhc3RlKCJwcm9ncmFtIixjb2wpKSsgYWVzKHNpemU9bmVzKSx0aXRsZSA9IHBhc3RlMCgiZ2VwIixjb2wpKQogIH0KYGBgCgpgYGB7cn0KcHJvZ3JhbXNfbWFpbl9wYXRod2F5cyA9IGxpc3QoZ2VwMSA9IDE6MiwgZ2VwMiA9IDE6MyxnZXAzID0gMTo0KQpgYGAKCgpgYGB7cn0KZ2VwX3Njb3JlcyA9IHB5JGdlcF9zY29yZXM4CnVzYWdlX25vcm0gPSBweSR1c2FnZV9ub3JtOApgYGAKCiMgcHJvZ3JtYXMgZnJvbSBOTUYKYGBge3IgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTksIGZpZy53aWR0aD0xMiwgcmVzdWx0cz0nYXNpcyd9CmNvbG5hbWVzKHVzYWdlX25vcm0pID0gcGFzdGUwKCJnZXAiLDE6OCkKI2FkZCBlYWNoIG1ldGFnZW5lIHRvIG1ldGFkYXRhCmZvciAoaSAgaW4gMTpuY29sKHVzYWdlX25vcm0gKSkgewogIG1ldGFnZW5lX21ldGFkYXRhID0gdXNhZ2Vfbm9ybSBbLGksZHJvcD1GXQogIGx1bmcgPSBBZGRNZXRhRGF0YShvYmplY3QgPSBsdW5nLG1ldGFkYXRhID0gbWV0YWdlbmVfbWV0YWRhdGEsY29sLm5hbWUgPSBjb2xuYW1lcyh1c2FnZV9ub3JtKVtpXSkKfQoKRmVhdHVyZVBsb3Qob2JqZWN0ID0gbHVuZyxmZWF0dXJlcyA9IGNvbG5hbWVzKHVzYWdlX25vcm0pLG5jb2wgPSAzKQoKYGBgCiMgcHJvZ3JtYXMgZnJvbSBOTUYgey50YWJzZXR9CgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KbWV0YWdlbmVzX21lYW5fY29tcGFyZShkYXRhc2V0ID0gbHVuZyx0aW1lLnBvaW50X3ZhciA9ICJ0aW1lLnBvaW50IixwcmVmaXggPSAicGF0aWVudCIscGF0aWVudC5pZGVudF92YXIgPSAicGF0aWVudC5pZGVudCIscHJlX29uID0gYygicHJlLXRyZWF0bWVudCIsIm9uLXRyZWF0bWVudCIpLHRlc3QgPSAid2lsY294LnRlc3QiLHByb2dyYW1zID0gY29sbmFtZXModXNhZ2Vfbm9ybSlbMTo1XSx3aXRob3V0X3NwbGl0ID0gRikKYGBgCgojIENhbGN1bGF0ZSB1c2FnZSBieSBjb3VudHMgYmVmb3JlIEhhcm1vbnkKYGBge3IgZWNobz1UUlVFLCByZXN1bHRzPSdhc2lzJ30KIyBnZXQgZXhwcmVzc2lvbiB3aXRoIGdlbmVzIGluIGNubWYgaW5wdXQKZ2VuZXMgPSByb3duYW1lcyhnZXBfc2NvcmVzKQpsdW5nX2V4cHJlc3Npb24gPSB0KGFzLm1hdHJpeChHZXRBc3NheURhdGEobHVuZyxzbG90PSdkYXRhJykpKSAKbHVuZ19leHByZXNzaW9uID0gMioqbHVuZ19leHByZXNzaW9uICNjb252ZXJ0IGZyb20gbG9nMih0cG0rMSkgdG8gdHBtCmx1bmdfZXhwcmVzc2lvbiA9IGx1bmdfZXhwcmVzc2lvbi0xCmx1bmdfZXhwcmVzc2lvbiA9IGx1bmdfZXhwcmVzc2lvblssZ2VuZXNdICU+JSBhcy5kYXRhLmZyYW1lKCkKCmFsbF8wX2dlbmVzID0gY29sbmFtZXMobHVuZ19leHByZXNzaW9uKVtjb2xTdW1zKGx1bmdfZXhwcmVzc2lvbj09MCwgbmEucm09VFJVRSk9PW5yb3cobHVuZ19leHByZXNzaW9uKV0gI2RlbGV0ZSByb3dzIHRoYXQgaGF2ZSBhbGwgMApnZW5lcyA9IGdlbmVzWyFnZW5lcyAlaW4lIGFsbF8wX2dlbmVzXQpsdW5nX2V4cHJlc3Npb24gPSBsdW5nX2V4cHJlc3Npb25bLCFjb2xuYW1lcyhsdW5nX2V4cHJlc3Npb24pICVpbiUgYWxsXzBfZ2VuZXNdCmdjKHZlcmJvc2UgPSBGKQpgYGAKCgpgYGB7cHl0aG9ufQoKbHVuZ19leHByZXNzaW9uID0gci5sdW5nX2V4cHJlc3Npb24KZ2VuZXMgPSByLmdlbmVzCnVzYWdlX2J5X2NhbGMgPSBnZXRfdXNhZ2VfZnJvbV9zY29yZShjb3VudHM9bHVuZ19leHByZXNzaW9uLHRwbT1sdW5nX2V4cHJlc3Npb24sZ2VuZXM9Z2VuZXMsY25tZl9vYmo9Y25tZl9vYmosaz04LHN1bVRvMT1UcnVlKQp1c2FnZV9ieV9jYWxjX3Vubm9ybSA9IGdldF91c2FnZV9mcm9tX3Njb3JlKGNvdW50cz1sdW5nX2V4cHJlc3Npb24sdHBtPWx1bmdfZXhwcmVzc2lvbixnZW5lcz1nZW5lcyxjbm1mX29iaj1jbm1mX29iaixrPTgsc3VtVG8xPUZhbHNlKQoKYGBgCmBgYHtyfQp1c2FnZV9ieV9jYWxjID0gcHkkdXNhZ2VfYnlfY2FsYwp1c2FnZV9ieV9jYWxjX3Vubm9ybSA9cHkkdXNhZ2VfYnlfY2FsY191bm5vcm0KYGBgCgpgYGB7cn0KY29sbmFtZXModXNhZ2VfYnlfY2FsYykgPSBjKCJhdXRvaW1tdW5lIiwiVE5GYS5ORmtCIiwgImh5cG94aWEiLCJoeXBveGlhMiIsICJjZWxsX2N5Y2xlMSIsICJjZWxsX2N5Y2xlMiIsIklORmciLCJ1bmtub3duIikKYGBgCgoKYGBge3IgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTksIGZpZy53aWR0aD0xMiwgcmVzdWx0cz0nYXNpcyd9CiMgY29sbmFtZXModXNhZ2VfYnlfY2FsYykgPSBwYXN0ZTAoImdlcCIsMTo4KQojYWRkIGVhY2ggbWV0YWdlbmUgdG8gbWV0YWRhdGEKZm9yIChpICBpbiAxOm5jb2wodXNhZ2VfYnlfY2FsYyApKSB7CiAgbWV0YWdlbmVfbWV0YWRhdGEgPSB1c2FnZV9ieV9jYWxjIFssaSxkcm9wPUZdCiAgbHVuZyA9IEFkZE1ldGFEYXRhKG9iamVjdCA9IGx1bmcsbWV0YWRhdGEgPSBtZXRhZ2VuZV9tZXRhZGF0YSxjb2wubmFtZSA9IGNvbG5hbWVzKHVzYWdlX2J5X2NhbGMpW2ldKQp9CgpGZWF0dXJlUGxvdChvYmplY3QgPSBsdW5nLGZlYXR1cmVzID0gY29sbmFtZXModXNhZ2VfYnlfY2FsYyksbmNvbCA9IDMpCgpgYGAKCmBgYHtyfQpjb3JfcmVzID0gY29yKGdlcF9zY29yZXMpCmJyZWFrcyA8LSBjKHNlcSgtMSwxLGJ5PTAuMDEpKQpjb2xvcnNfZm9yX3Bsb3QgPC0gY29sb3JSYW1wUGFsZXR0ZShjb2xvcnMgPSBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKShuID0gbGVuZ3RoKGJyZWFrcykpCgpwaHQgPSBwaGVhdG1hcChjb3JfcmVzLGNvbG9yID0gY29sb3JzX2Zvcl9wbG90LGJyZWFrcyA9IGJyZWFrcykKcHJpbnRfdGFiKHBodCx0aXRsZSA9ICJjb3JyZWxhdGlvbiIpCmBgYAoKYGBge3J9Cmdyb3Vwc19saXN0ID0gYyg1LDYpCnBhdGllbnRzX2dlcHMgPSB1bmlvbl9wcm9ncmFtcyhncm91cHNfbGlzdCA9IGdyb3Vwc19saXN0LGFsbF9tZXRhZ2VuZXMgPSBwYXRpZW50c19nZXBzKQpgYGAKCiMgUmVndWxhdGlvbiB7LnRhYnNldH0KCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQptZXRhZ2VuZXNfbWVhbl9jb21wYXJlKGRhdGFzZXQgPSBsdW5nLHRpbWUucG9pbnRfdmFyID0gInRpbWUucG9pbnQiLHByZWZpeCA9ICJwYXRpZW50IixwYXRpZW50LmlkZW50X3ZhciA9ICJwYXRpZW50LmlkZW50IixwcmVfb24gPSBjKCJwcmUtdHJlYXRtZW50Iiwib24tdHJlYXRtZW50IiksdGVzdCA9ICJ3aWxjb3gudGVzdCIscHJvZ3JhbXMgPSBjb2xuYW1lcyh1c2FnZV9ieV9jYWxjKVsxOjVdLHdpdGhvdXRfc3BsaXQgPSBGKQpgYGAKCgojIHByb2dyYW1zIExFIGdlbmVzIHsudGFic2V0fQpgYGB7ciByZXN1bHRzPSdhc2lzJ30KCnByb2dyYW1zX21haW5fcGF0aHdheXNfbmFtZXMgPSBsaXN0KCkKICBmb3IgKGNvbCBpbiBzZXFfYWxvbmcoZ2VwX3RwbTgpKSB7CiAgICAgcmFua2VkX3ZlYyA9IGdlcF90cG04Wyxjb2xdICU+JSBzZXROYW1lcyhyb3duYW1lcyhnZXBfdHBtOCkpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUUlVFKSAKICAgICBoeXBfb2JqIDwtIGh5cGVSX2Znc2VhKHJhbmtlZF92ZWMsIGdlbmVzZXRzKQogICAgIHByb2dyYW1zX21haW5fcGF0aHdheXNfbmFtZXNbW2NvbF1dID0gIGh5cF9vYmokZGF0YVtwcm9ncmFtc19tYWluX3BhdGh3YXlzW1tjb2xdXSwibGFiZWwiLGRyb3A9VF0KICAgICBmb3IgKHBhdGh3YXlfbnVtIGluIHByb2dyYW1zX21haW5fcGF0aHdheXNbW2NvbF1dKSB7CiAgICAgICAgbGVfZ2VuZXMgPSAgaHlwX29iaiRkYXRhW3BhdGh3YXlfbnVtLCxkcm9wPUZdICU+JSBwdWxsKCJsZSIpICU+JSBzdHJzcGxpdCgiLCIpICU+JSB1bmxpc3QoKQogICAgICAgIHNjb3JlID0gRmV0Y2hEYXRhKG9iamVjdCA9IGx1bmcsdmFycyA9IGxlX2dlbmVzKSAlPiUgcm93TWVhbnMoKQogICAgICAgIHBhdGh3YXlfbmFtZSA9IHBhc3RlMChoeXBfb2JqJGRhdGFbcGF0aHdheV9udW0sImxhYmVsIixkcm9wPUZdLCJfbGUiKQogICAgICAgIGx1bmc9QWRkTWV0YURhdGEobHVuZyxzY29yZSxjb2wubmFtZSA9IHBhdGh3YXlfbmFtZSkKICAgICAgICBwcmludF90YWIoRmVhdHVyZVBsb3Qob2JqZWN0ID0gbHVuZyxmZWF0dXJlcyA9IHBhdGh3YXlfbmFtZSksdGl0bGUgPSBwYXRod2F5X25hbWUpCiAgICAgfQogIH0KYGBgCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KcHJvZ3JhbXNfbWFpbl9wYXRod2F5c19uYW1lcyA9IGxpc3QoKQogIGZvciAoY29sIGluIHNlcV9hbG9uZyhnZXBfc2NvcmVzKSkgewogICAgIHJhbmtlZF92ZWMgPSBnZXBfc2NvcmVzWyxjb2xdICU+JSBzZXROYW1lcyhyb3duYW1lcyhnZXBfc2NvcmVzKSkgJT4lIHNvcnQoZGVjcmVhc2luZyA9IFRSVUUpIAogICAgIGh5cF9vYmogPC0gaHlwZVJfZmdzZWEocmFua2VkX3ZlYywgZ2VuZXNldHMpCiAgICAgcHJvZ3JhbXNfbWFpbl9wYXRod2F5c19uYW1lc1tbY29sXV0gPSAgaHlwX29iaiRkYXRhW3Byb2dyYW1zX21haW5fcGF0aHdheXNbW2NvbF1dLCJsYWJlbCIsZHJvcD1UXQogICAgIGZvciAocGF0aHdheV9udW0gaW4gcHJvZ3JhbXNfbWFpbl9wYXRod2F5c1tbY29sXV0pIHsKICAgICAgICBsZV9nZW5lcyA9ICBoeXBfb2JqJGRhdGFbcGF0aHdheV9udW0sLGRyb3A9Rl0gJT4lIHB1bGwoImxlIikgJT4lIHN0cnNwbGl0KCIsIikgJT4lIHVubGlzdCgpCiAgICAgICAgc2NvcmUgPSBGZXRjaERhdGEob2JqZWN0ID0gbHVuZyx2YXJzID0gbGVfZ2VuZXMpICU+JSByb3dNZWFucygpCiAgICAgICAgcGF0aHdheV9uYW1lID0gcGFzdGUwKGh5cF9vYmokZGF0YVtwYXRod2F5X251bSwibGFiZWwiLGRyb3A9Rl0sIl9sZSIpCiAgICAgICAgbHVuZz1BZGRNZXRhRGF0YShsdW5nLHNjb3JlLGNvbC5uYW1lID0gcGF0aHdheV9uYW1lKQogICAgICAgIHByaW50X3RhYihGZWF0dXJlUGxvdChvYmplY3QgPSBsdW5nLGZlYXR1cmVzID0gcGF0aHdheV9uYW1lKSx0aXRsZSA9IHBhdGh3YXlfbmFtZSkKICAgICB9CiAgfQpgYGAKYGBge3J9CmdlbmVzID0gZ2VwX3Njb3Jlc1ssMl0gJT4lIHNldE5hbWVzKHJvd25hbWVzKGdlcF9zY29yZXMpKSAlPiUgc29ydChkZWNyZWFzaW5nID0gVFJVRSkgJT4lIG5hbWVzCnNjb3JlID0gRmV0Y2hEYXRhKG9iamVjdCA9IGx1bmcsdmFycyA9IGdlbmVzWzE6MTAwXSkgJT4lIHJvd01lYW5zKCkKcGF0aHdheV9uYW1lID0gInRvcF8xMDBfbmZrYiIKbHVuZz1BZGRNZXRhRGF0YShsdW5nLHNjb3JlLGNvbC5uYW1lID0gcGF0aHdheV9uYW1lKQpgYGAKCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQptZXRhZ2VuZXNfbWVhbl9jb21wYXJlKGRhdGFzZXQgPSBsdW5nLHRpbWUucG9pbnRfdmFyID0gInRpbWUucG9pbnQiLHByZWZpeCA9ICJwYXRpZW50IixwYXRpZW50LmlkZW50X3ZhciA9ICJwYXRpZW50LmlkZW50IixwcmVfb24gPSBjKCJwcmUtdHJlYXRtZW50Iiwib24tdHJlYXRtZW50IiksdGVzdCA9ICJ3aWxjb3gudGVzdCIscHJvZ3JhbXMgPSBwYXRod2F5X25hbWUsd2l0aG91dF9zcGxpdCA9IEYpCmBgYAojIHByb2dyYW1zIExFIGdlbmVzIHJlZ3VsYXRpb24gey50YWJzZXR9CgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KbWV0YWdlbmVzX21lYW5fY29tcGFyZShkYXRhc2V0ID0gbHVuZyx0aW1lLnBvaW50X3ZhciA9ICJ0aW1lLnBvaW50IixwcmVmaXggPSAicGF0aWVudCIscGF0aWVudC5pZGVudF92YXIgPSAicGF0aWVudC5pZGVudCIscHJlX29uID0gYygicHJlLXRyZWF0bWVudCIsIm9uLXRyZWF0bWVudCIpLHRlc3QgPSAid2lsY294LnRlc3QiLHByb2dyYW1zID0gcHJvZ3JhbXNfbWFpbl9wYXRod2F5c19uYW1lcyAlPiUgdW5saXN0KCkgJT4lIHBhc3RlMCgiX2xlIiksd2l0aG91dF9zcGxpdCA9IEYpCmBgYAoKCiMgVG9wIHByb2dyYW0gMiBnZW5lcyBleHByZXNzaW9uIGNvcnJlbGF0aW9uCmBgYHtyfQp0b3Bfb3QgPSBnZXBfc2NvcmVzIFtvcmRlcihnZXBfc2NvcmVzIFssMl0sZGVjcmVhc2luZyA9IFQpLDIsZHJvcCA9IEZdJT4lIGhlYWQoMjAwKSAlPiUgcm93bmFtZXMoKQoKYW5ub3RhdGlvbiA9IHBsb3RfZ2VuZXNfY29yKGRhdGFzZXQgPSB4ZW5vLGdlbmVJZHMgPSB0b3Bfb3QsaGVpZ2h0ID0gMi44KQoKYGBgCiMgY2x1c3RlciBwcm9ncmFtIGV4cHJlc3Npb24gey50YWJzZXR9CgpgYGB7ciByZXN1bHRzPSdhc2lzJyxmaWcud2lkdGg9MTR9CmZvciAoY2hvc2VuX2NsdXN0ZXJzIGluIDE6bGVuZ3RoKHVuaXF1ZShhbm5vdGF0aW9uJGNsdXN0ZXIpKSkgewogIGNob3Nlbl9nZW5lcyA9IGFubm90YXRpb24gJT4lIGRwbHlyOjpmaWx0ZXIoY2x1c3RlciA9PSBjaG9zZW5fY2x1c3RlcnMpICU+JSByb3duYW1lcygpICN0YWtlIHJlbGV2YW50IGdlbmVzCiAgIyBwcmludChjaG9zZW5fZ2VuZXMpCiAgaHlwX29iaiA8LSBoeXBlUihjaG9zZW5fZ2VuZXMsIGdlbmVzZXRzLCB0ZXN0ID0gImh5cGVyZ2VvbWV0cmljIiwgZmRyPTEsIHBsb3R0aW5nPUYsYmFja2dyb3VuZCA9IHJvd25hbWVzKHBhdGllbnRzX2dlcHMpKQoKICAgc2NvcmVzQW5kSW5kaWNlcyA8LSBnZXRQYXRod2F5U2NvcmVzKGx1bmdAYXNzYXlzJFJOQUBkYXRhLCBjaG9zZW5fZ2VuZXMpCiAgbHVuZz1BZGRNZXRhRGF0YShsdW5nLHNjb3Jlc0FuZEluZGljZXMkcGF0aHdheVNjb3JlcyxwYXN0ZTAoImNsdXN0ZXIiLGNob3Nlbl9jbHVzdGVycykpCgogIAogIHByaW50X3RhYihwbHQgPSAKICAgICAgICAgICAgICBoeXBfZG90cyhoeXBfb2JqLHNpemVfYnkgPSAibm9uZSIsdGl0bGUgPSBwYXN0ZTAoImNsdXN0ZXIiLGNob3Nlbl9jbHVzdGVycykpKwogICAgICAgICAgICAgIEZlYXR1cmVQbG90KG9iamVjdCA9IGx1bmcsZmVhdHVyZXMgPSBwYXN0ZTAoImNsdXN0ZXIiLGNob3Nlbl9jbHVzdGVycykpLAogICAgICAgICAgICB0aXRsZSA9IGNob3Nlbl9jbHVzdGVycykKICAKICBjb3JfcmVzID0gY29yKGx1bmckaW50ZXJmZXJvbl9saWtlLGx1bmdbW3Bhc3RlMCgiY2x1c3RlciIsY2hvc2VuX2NsdXN0ZXJzKV1dKQojIHByaW50KHBhc3RlKCJjb3JyZWxhdGlvbiBvZiBUTkZhIHByb2dyYW0gdG8iLCBwYXN0ZTAoImNsdXN0ZXIiLGNob3Nlbl9jbHVzdGVycyksIjoiLCBjb3JfcmVzKSkKCn0KCmBgYAoKCiMgY2x1c3RlciBwcm9ncmFtIHJlZ3VsYXRpb24gey50YWJzZXR9CgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KbWV0YWdlbmVzX21lYW5fY29tcGFyZShkYXRhc2V0ID0gbHVuZyx0aW1lLnBvaW50X3ZhciA9ICJ0aW1lLnBvaW50IixwcmVmaXggPSAicGF0aWVudCIscGF0aWVudC5pZGVudF92YXIgPSAicGF0aWVudC5pZGVudCIscHJlX29uID0gYygicHJlLXRyZWF0bWVudCIsIm9uLXRyZWF0bWVudCIpLHRlc3QgPSAid2lsY294LnRlc3QiLHByb2dyYW1zID0gIkNGTEFSIix3aXRob3V0X3NwbGl0ID0gRikKYGBgCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KbWV0YWdlbmVzX21lYW5fY29tcGFyZShkYXRhc2V0ID0gbHVuZyx0aW1lLnBvaW50X3ZhciA9ICJ0aW1lLnBvaW50IixwcmVmaXggPSAicGF0aWVudCIscGF0aWVudC5pZGVudF92YXIgPSAicGF0aWVudC5pZGVudCIscHJlX29uID0gYygicHJlLXRyZWF0bWVudCIsIm9uLXRyZWF0bWVudCIpLHRlc3QgPSAid2lsY294LnRlc3QiLHByb2dyYW1zID0gIk5GS0IxIix3aXRob3V0X3NwbGl0ID0gRikKYGBgCgojIGNsdXN0ZXIgcHJvZ3JhbSBlbnJpY2htZW50IHsudGFic2V0fQpgYGB7cn0KdG9wX290ID0gcGF0aWVudHNfZ2Vwc1tvcmRlcihwYXRpZW50c19nZXBzWywxXSxkZWNyZWFzaW5nID0gVCksMixkcm9wID0gRl0lPiUgaGVhZCgxMCkgJT4lIHJvd25hbWVzKCkKCmNob3Nlbl9nZW5lcyA9IHRvcF9vdAogc2NvcmVzQW5kSW5kaWNlcyA8LSBnZXRQYXRod2F5U2NvcmVzKGx1bmdAYXNzYXlzJFJOQUBkYXRhLCBjaG9zZW5fZ2VuZXMpCiAgbHVuZz1BZGRNZXRhRGF0YShsdW5nLHNjb3Jlc0FuZEluZGljZXMkcGF0aHdheVNjb3JlcywiaW1tdW5lX2dlcCIpCgogIAogIAogIGNvcl9yZXMgPSBjb3IobHVuZyRpbnRlcmZlcm9uX2xpa2UsbHVuZ1tbImltbXVuZV9nZXAiXV0pCnByaW50KHBhc3RlKCJjb3JyZWxhdGlvbiBvZiBUTkZhIHByb2dyYW0gdG8iLCAiaW1tdW5lX2dlcCIsIjoiLCBjb3JfcmVzKSkKYGBgCmBgYHtyfQptZXRhZ2VuZXNfbWVhbl9jb21wYXJlKGRhdGFzZXQgPSBsdW5nLHRpbWUucG9pbnRfdmFyID0gInRpbWUucG9pbnQiLHByZWZpeCA9ICJwYXRpZW50IixwYXRpZW50LmlkZW50X3ZhciA9ICJwYXRpZW50LmlkZW50IixwcmVfb24gPSBjKCJwcmUtdHJlYXRtZW50Iiwib24tdHJlYXRtZW50IiksdGVzdCA9ICJ3aWxjb3gudGVzdCIscHJvZ3JhbXMgPSAiaW1tdW5lX2dlcCIsd2l0aG91dF9zcGxpdCA9IEYpCmBgYAoKYGBge3B5dGhvbn0KaW1wb3J0IGFubmRhdGEgYXMgYWQKaW1wb3J0IHNjYW5weSBhcyBzYwppbXBvcnQgbnVtcHkgYXMgbnAKZnJvbSBza2xlYXJuLmRlY29tcG9zaXRpb24gaW1wb3J0IG5vbl9uZWdhdGl2ZV9mYWN0b3JpemF0aW9uCmltcG9ydCBwYW5kYXMgYXMgcGQKCgpsdW5nX2V4cHJlc3Npb24gPSByLmx1bmdfZXhwcmVzc2lvbgpnZW5lcyA9IHIuZ2VuZXMKY291bnRzPWx1bmdfZXhwcmVzc2lvbgp0cG09bHVuZ19leHByZXNzaW9uCgpjb3VudHNfYWRhdGEgPSBhZC5Bbm5EYXRhKGNvdW50cykKdHBtX2FkYXRhID0gYWQuQW5uRGF0YSh0cG0pCgoKCiNnZXQgbWF0cmljZXMKbm9ybV9jb3VudHMgPSBnZXRfbm9ybV9jb3VudHMoY291bnRzPWNvdW50c19hZGF0YSx0cG09dHBtX2FkYXRhLGhpZ2hfdmFyaWFuY2VfZ2VuZXNfZmlsdGVyPW5wLmFycmF5KGdlbmVzKSkgI25vcm0gY291bnRzIGxpa2UgY25tZgpub3JtX2NvdW50cyA9IG5vcm1fY291bnRzLnRvX2RmKCkKYGBgCmBgYHtyfQpub3JtX2NvdW50cyA9IHB5JG5vcm1fY291bnRzICU+JSBhcy5kYXRhLmZyYW1lKCkKYGBgCgpgYGB7cn0KZGYgPSBnZXBfc2NvcmVzOAp0b3AgPSAyMDAKcHJvZ3JhbV9uYW1lID1wYXN0ZTAoInRvcF8iLCB0b3AsICJfZ2VuZXMiKQpleHByZXNzaW9uID0gbHVuZ19leHByZXNzaW9uCmdlbmVzID0gZGZbLDJdICU+JSBzZXROYW1lcyhyb3duYW1lcyhkZikpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUUlVFKSAlPiUgbmFtZXMoKQpsZV9nZW5lcwoKIyBtdWx0ID0gYXMubWF0cml4KHQoZGZbZ2VuZXNbMTp0b3BdLDIsZHJvcD1GXSkpICUqJSAobHVuZ0Bhc3NheXMkUk5BQGRhdGFbZ2VuZXNbMTp0b3BdLF0gJT4lIGFzLm1hdHJpeCgpKTsgbXVsdCA9IG11bHRbMSxdCm11bHQgPSAgKGV4cHJlc3Npb25bLGdlbmVzWzE6dG9wXV0gJT4lIGFzLm1hdHJpeCgpKSAlKiUgIGFzLm1hdHJpeCgoZGZbZ2VuZXNbMTp0b3BdLDIsZHJvcD1GXSkpCiMgbXVsdCA9ICAoZXhwcmVzc2lvblssbGVfZ2VuZXNdICU+JSBhcy5tYXRyaXgoKSkgJSolICBhcy5tYXRyaXgoKGRmW2xlX2dlbmVzLDIsZHJvcD1GXSkpCgpjb3JfcmVzID0gY29yKHVzYWdlX2J5X2NhbGNbLDJdLG11bHQpCnByaW50KHBhc3RlKCJjb3JyZWxhdGlvbiBvZiBUTkZhL05Ga0IgcHJvZ3JhbSB0byIsIHByb2dyYW1fbmFtZSwiOiIsIGNvcl9yZXMpKQpgYGAKCmBgYHtyfQpkZiA9IGdlcF9zY29yZXM4CmV4cHJlc3Npb24gID0gbHVuZ19leHByZXNzaW9uCnRvcCA9IDIwMApwcm9ncmFtX25hbWUgPXBhc3RlMCgidG9wXyIsIHRvcCwgIl9nZW5lcyIpCnByb2duYW1faW5kZXg9IDIKdG9wX2dlbmVzID0gZGZbLHByb2duYW1faW5kZXhdICU+JSBzZXROYW1lcyhyb3duYW1lcyhkZikpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUUlVFKSAlPiUgbmFtZXMoKQoKbXVsdCA9ICAoZXhwcmVzc2lvblssdG9wX2dlbmVzWzE6dG9wXV0gJT4lIGFzLm1hdHJpeCgpKSAgJSolIGFzLm1hdHJpeChkZlt0b3BfZ2VuZXNbMTp0b3BdLHByb2duYW1faW5kZXgsZHJvcD1GXSkKCmF2Z190cG0gPSBleHByZXNzaW9uWyx0b3BfZ2VuZXNbMTp0b3BdXSAlPiUgcm93TWVhbnMoKQphdmdfdHBtX2FsbF9nZW5lcyA9IGx1bmdAYXNzYXlzJFJOQUBkYXRhW3RvcF9nZW5lc1sxOnRvcF0sXSAlPiUgMl4uICU+JSBtYWdyaXR0cjo6c3VidHJhY3QoMSkgJT4lICBjb2xNZWFucygpCgpjb3IodXNhZ2VfYnlfY2FsY1sscHJvZ25hbV9pbmRleF0sbXVsdCkKY29yKHVzYWdlX2J5X2NhbGNbLHByb2duYW1faW5kZXhdLGF2Z190cG0pCmNvcih1c2FnZV9ieV9jYWxjWyxwcm9nbmFtX2luZGV4XSxhdmdfdHBtX2FsbF9nZW5lcykKCgpgYGAKCmBgYHtyfQptZXRhZ2VuZXNfbWVhbl9jb21wYXJlKGRhdGFzZXQgPSBsdW5nLHRpbWUucG9pbnRfdmFyID0gInRpbWUucG9pbnQiLHByZWZpeCA9ICJwYXRpZW50IixwYXRpZW50LmlkZW50X3ZhciA9ICJwYXRpZW50LmlkZW50IixwcmVfb24gPSBjKCJwcmUtdHJlYXRtZW50Iiwib24tdHJlYXRtZW50IiksdGVzdCA9ICJ3aWxjb3gudGVzdCIscHJvZ3JhbXMgPSAiaW1tdW5lX2dlcF9tdWx0Iix3aXRob3V0X3NwbGl0ID0gRikKYGBgCmBgYHtyIGVjaG89VFJVRSwgcmVzdWx0cz0nYXNpcyd9CiMgZ2V0IGV4cHJlc3Npb24gd2l0aCBnZW5lcyBpbiBjbm1mIGlucHV0Cmx1bmcgPSBGaW5kVmFyaWFibGVGZWF0dXJlcyhvYmplY3QgPSBsdW5nLG5mZWF0dXJlcyA9IDIwMDApCmdlbmVzID0gcm93bmFtZXMobHVuZylbcm93bmFtZXMobHVuZykgJWluJSBWYXJpYWJsZUZlYXR1cmVzKG9iamVjdCA9IHhlbm8pWzE6MjAwMF1dCgpsdW5nX2V4cHJlc3Npb24gPSB0KGFzLm1hdHJpeChHZXRBc3NheURhdGEobHVuZyxzbG90PSdkYXRhJykpKSAKbHVuZ19leHByZXNzaW9uID0gMioqbHVuZ19leHByZXNzaW9uICNjb252ZXJ0IGZyb20gbG9nMih0cG0rMSkgdG8gdHBtCmx1bmdfZXhwcmVzc2lvbiA9IGx1bmdfZXhwcmVzc2lvbi0xCmx1bmdfZXhwcmVzc2lvbiA9IGx1bmdfZXhwcmVzc2lvblssZ2VuZXNdICU+JSBhcy5kYXRhLmZyYW1lKCkKCmFsbF8wX2dlbmVzID0gY29sbmFtZXMobHVuZ19leHByZXNzaW9uKVtjb2xTdW1zKGx1bmdfZXhwcmVzc2lvbj09MCwgbmEucm09VFJVRSk9PW5yb3cobHVuZ19leHByZXNzaW9uKV0gI2RlbGV0ZSByb3dzIHRoYXQgaGF2ZSBhbGwgMApnZW5lcyA9IGdlbmVzWyFnZW5lcyAlaW4lIGFsbF8wX2dlbmVzXQpsdW5nX2V4cHJlc3Npb24gPSBsdW5nX2V4cHJlc3Npb25bLCFjb2xuYW1lcyhsdW5nX2V4cHJlc3Npb24pICVpbiUgYWxsXzBfZ2VuZXNdCmdjKHZlcmJvc2UgPSBGKQpgYGAKYGBge3B5dGhvbn0KCmx1bmdfZXhwcmVzc2lvbiA9IHIubHVuZ19leHByZXNzaW9uCmdlbmVzID0gci5nZW5lcwojIGdlcF9zY29yZXMgPSByLnBhdGllbnRzX2dlcHMKdXNhZ2VfYnlfY2FsYyA9IGdldF91c2FnZV9mcm9tX3Njb3JlKGNvdW50cz1sdW5nX2V4cHJlc3Npb24sdHBtPWx1bmdfZXhwcmVzc2lvbixnZW5lcz1nZW5lcyxjbm1mX29iaj1jbm1mX29iaixrPTgsc3VtVG8xPUZhbHNlKQpgYGAKCmBgYHtweXRob259CmRlZiBnZXRfdXNhZ2VfZnJvbV9zY29yZShjb3VudHMsdHBtLCBnZW5lcyxjbm1mX29iaixrLCBzdW1UbzEgPSBUcnVlLGRvX25vcm1fY291bnRzID0gVHJ1ZSk6ICNiYXNlZCBvbiAnY29uc2Vuc3VzJyBtZXRob2QKICAgICAgaW1wb3J0IGFubmRhdGEgYXMgYWQKICAgICAgaW1wb3J0IHNjYW5weSBhcyBzYwogICAgICBpbXBvcnQgbnVtcHkgYXMgbnAKICAgICAgZnJvbSBza2xlYXJuLmRlY29tcG9zaXRpb24gaW1wb3J0IG5vbl9uZWdhdGl2ZV9mYWN0b3JpemF0aW9uCiAgICAgIGltcG9ydCBwYW5kYXMgYXMgcGQKICAgICAgY291bnRzX2FkYXRhID0gYWQuQW5uRGF0YShjb3VudHMpCiAgICAgIHRwbV9hZGF0YSA9IGFkLkFubkRhdGEodHBtKQogICAgICAKICAgICAgI2dldCBtYXRyaWNlcwogICAgICBpZihkb19ub3JtX2NvdW50cyk6CiAgICAgICAgbm9ybV9jb3VudHMgPSBnZXRfbm9ybV9jb3VudHMoY291bnRzPWNvdW50c19hZGF0YSx0cG09dHBtX2FkYXRhLGhpZ2hfdmFyaWFuY2VfZ2VuZXNfZmlsdGVyPW5wLmFycmF5KGdlbmVzKSkgI25vcm0gY291bnRzIGxpa2UgY25tZgogICAgICBlbHNlOgogICAgICAgIG5vcm1fY291bnRzID0gYWQuQW5uRGF0YShjb3VudHMpCiAgICAgIHNwZWN0cmFfb3JpZ2luYWwgPSBjbm1mX29iai5nZXRfbWVkaWFuX3NwZWN0cmEoaz1rKSAjZ2V0IHNjb3JlIAogICAgICAKICAgICAgIyBmaWx0ZXIgCiAgICAgIHNwZWN0cmEgPSBzcGVjdHJhX29yaWdpbmFsW3NwZWN0cmFfb3JpZ2luYWwuY29sdW1ucy5pbnRlcnNlY3Rpb24oZ2VuZXMpXSAjcmVtb3ZlIGdlbmVzIG5vdCBpbiBAZ2VuZXMKICAgICAgbm9ybV9jb3VudHMgPSBub3JtX2NvdW50c1s6LCBzcGVjdHJhLmNvbHVtbnNdLmNvcHkoKSAjcmVtb3ZlIGdlbmVzIG5vdCBpbiBzcGVjdHJhCiAgICAgIHNwZWN0cmEgPSBzcGVjdHJhLlQucmVpbmRleChub3JtX2NvdW50cy50b19kZigpLmNvbHVtbnMpLlQgI3Jlb3JkZXIgc3BlY3RyYSBnZW5lcyBsaWtlIG5vcm1fY291bnRzCiAgICAgIAogICAgICAjIGNhbGN1bGF0ZSB1c2FnZQogICAgICB1c2FnZV9ieV9jYWxjLF8sXyA9IG5vbl9uZWdhdGl2ZV9mYWN0b3JpemF0aW9uKFg9bm9ybV9jb3VudHMuWCwgSCA9IHNwZWN0cmEudmFsdWVzLCB1cGRhdGVfSD1GYWxzZSxuX2NvbXBvbmVudHMgPSBrLG1heF9pdGVyPTEwMDAsaW5pdCA9J3JhbmRvbScpCiAgICAgIHVzYWdlX2J5X2NhbGMgPSBwZC5EYXRhRnJhbWUodXNhZ2VfYnlfY2FsYywgaW5kZXg9Y291bnRzLmluZGV4LCBjb2x1bW5zPXNwZWN0cmEuaW5kZXgpICNpbnNlcnQgdG8gZGYrYWRkIG5hbWVzCiAgICAgIAogICAgICAjbm9ybWFsaXplCiAgICAgIGlmKHN1bVRvMSk6CiAgICAgICAgICB1c2FnZV9ieV9jYWxjID0gdXNhZ2VfYnlfY2FsYy5kaXYodXNhZ2VfYnlfY2FsYy5zdW0oYXhpcz0xKSwgYXhpcz0wKSAjIHN1bSByb3dzIHRvIDEgYW5kIGFzc2lnbiB0byBtYWluIGRmCiAgICAgIAogICAgICAjIHJlb3JkZXIKICAgICAgICAjIGdldCBvcmlnaW5hbCBvcmRlcgogICAgICBvcmlnaW5hbF9ub3JtX2NvdW50cyA9IHNjLnJlYWQoY25tZl9vYmoucGF0aHNbJ25vcm1hbGl6ZWRfY291bnRzJ10pCiAgICAgIHVzYWdlX2J5X2NhbGNfb3JpZ2luYWwsXyxfID0gbm9uX25lZ2F0aXZlX2ZhY3Rvcml6YXRpb24oWD1vcmlnaW5hbF9ub3JtX2NvdW50cy5YLCBIID0gc3BlY3RyYV9vcmlnaW5hbC52YWx1ZXMsIHVwZGF0ZV9IPUZhbHNlLG5fY29tcG9uZW50cyA9IGssbWF4X2l0ZXI9MTAwMCxpbml0ID0ncmFuZG9tJykKICAgICAgdXNhZ2VfYnlfY2FsY19vcmlnaW5hbCA9IHBkLkRhdGFGcmFtZSh1c2FnZV9ieV9jYWxjX29yaWdpbmFsLCBpbmRleD1vcmlnaW5hbF9ub3JtX2NvdW50cy5vYnMuaW5kZXgsIGNvbHVtbnM9c3BlY3RyYV9vcmlnaW5hbC5pbmRleCkgIAogICAgICBub3JtX29yaWdpbmFsX3VzYWdlcyA9dXNhZ2VfYnlfY2FsY19vcmlnaW5hbC5kaXYodXNhZ2VfYnlfY2FsY19vcmlnaW5hbC5zdW0oYXhpcz0xKSwgYXhpcz0wKSAgICAgIAogICAgICByZW9yZGVyID0gbm9ybV9vcmlnaW5hbF91c2FnZXMuc3VtKGF4aXM9MCkuc29ydF92YWx1ZXMoYXNjZW5kaW5nPUZhbHNlKQogICAgICAgICNhcHBseQogICAgICB1c2FnZV9ieV9jYWxjID0gdXNhZ2VfYnlfY2FsYy5sb2NbOiwgcmVvcmRlci5pbmRleF0KICAgICAgcmV0dXJuKHVzYWdlX2J5X2NhbGMpCiAgICAKCmBgYAoKYGBge3B5dGhvbn0KCmx1bmdfZXhwcmVzc2lvbiA9IHIubHVuZ19leHByZXNzaW9uCmdlbmVzID0gci5nZW5lcwp1c2FnZV9ieV9jYWxjID0gZ2V0X3VzYWdlX2Zyb21fc2NvcmUoY291bnRzPWx1bmdfZXhwcmVzc2lvbix0cG09bHVuZ19leHByZXNzaW9uLGdlbmVzPWdlbmVzLGNubWZfb2JqPWNubWZfb2JqLGs9OCxzdW1UbzE9VHJ1ZSxkb19ub3JtX2NvdW50cz1GYWxzZSkKYGBgCgoKCmBgYHtyfQp1c2FnZV9ieV9jYWxjID0gcHkkdXNhZ2VfYnlfY2FsYwpncm91cHNfbGlzdCA9IGMoNCwzLDYpCnVzYWdlX2J5X2NhbGMgPSB1bmlvbl9wcm9ncmFtcyhncm91cHNfbGlzdCA9IGdyb3Vwc19saXN0LGFsbF9tZXRhZ2VuZXMgPSB1c2FnZV9ieV9jYWxjKQojIHVzYWdlX2J5X2NhbGMgPSBhcHBseSh1c2FnZV9ieV9jYWxjLCBNQVJHSU4gPSAxLCBzdW1fMl9vbmUpICU+JSB0KCkgJT4lIGFzLmRhdGEuZnJhbWUoKQp1c2FnZV9ieV9jYWxjID11c2FnZV9ieV9jYWxjICU+JSByZW5hbWUoY2VsbF9jeWNsZSA9IGdlcDQuMy42LCBoeXBveGlhX2xpa2UgPSBnZXAyLCBpbnRlcmZlcm9uX2xpa2UgPSBnZXAxLCBUTkZhID0gIGdlcDUsIElORjIgPSBnZXA3KQpgYGAKCmBgYHtyfQpsdW5nPUFkZE1ldGFEYXRhKGx1bmcsdXNhZ2VfYnlfY2FsY1ssMSxkcm9wPUZdLCJpbW11bmVfZ2VwX25vX3N1bTJvbmUiKQptZXRhZ2VuZXNfbWVhbl9jb21wYXJlKGRhdGFzZXQgPSBsdW5nLHRpbWUucG9pbnRfdmFyID0gInRpbWUucG9pbnQiLHByZWZpeCA9ICJwYXRpZW50IixwYXRpZW50LmlkZW50X3ZhciA9ICJwYXRpZW50LmlkZW50IixwcmVfb24gPSBjKCJwcmUtdHJlYXRtZW50Iiwib24tdHJlYXRtZW50IiksdGVzdCA9ICJ3aWxjb3gudGVzdCIscHJvZ3JhbXMgPSAiaW1tdW5lX2dlcF9ub19zdW0yb25lIix3aXRob3V0X3NwbGl0ID0gRikKYGBgCgojIFByb2dyYW0gMgpgYGB7cn0KY29sPTEKcmFua2VkX3ZlYyA9IGdlcF9zY29yZXM4WywgY29sXSAlPiUgc2V0TmFtZXMocm93bmFtZXMoZ2VwX3Njb3JlczgpKSAlPiUgc29ydChkZWNyZWFzaW5nID0gVFJVRSkKcHJpbnQgKHBhc3RlKCJydW5uaW5nIGdlcCIsY29sKSkKaHlwX29iaiA8LWh5cGVSX2Znc2VhKHJhbmtlZF92ZWMsIGdlbmVzZXRzX2dvLCB1cF9vbmx5ID0gVCkKCnByaW50KGh5cF9kb3RzKGh5cF9vYmosIHRpdGxlID0gcGFzdGUoInByb2dyYW0iLCBjb2wpLCBhYnJ2ID0gNzApICsgYWVzKHNpemUgPW5lcykpCiAgCmBgYAoKCmBgYHtyfQpjb2w9MgpyYW5rZWRfdmVjID0gZ2VwX3Njb3JlczhbLCBjb2xdICU+JSBzZXROYW1lcyhyb3duYW1lcyhnZXBfc2NvcmVzOCkpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUUlVFKQpwcmludCAocGFzdGUoInJ1bm5pbmcgZ2VwIixjb2wpKQpoeXBfb2JqIDwtaHlwZVJfZmdzZWEocmFua2VkX3ZlYywgZ2VuZXNldHNfZ28sIHVwX29ubHkgPSBUKQoKcHJpbnQoaHlwX2RvdHMoaHlwX29iaiwgdGl0bGUgPSBwYXN0ZSgicHJvZ3JhbSIsIGNvbCksIGFicnYgPSA3MCkgKyBhZXMoc2l6ZSA9bmVzKSkKICAKYGBgCgoKYGBge3J9CnBhdGh3YXlfbnVtID0gMQpsZV9nZW5lcyA9IGh5cF9vYmokYXMuZGF0YS5mcmFtZSgpW3BhdGh3YXlfbnVtLCJsZSJdICU+JSBzdHJzcGxpdCgiLCIpICU+JSB1bmxpc3QoKQpzY29yZSA9IChsdW5nX2V4cHJlc3Npb25bLCBsZV9nZW5lc10gJT4lIGFzLm1hdHJpeCgpKSAgJSolIGFzLm1hdHJpeChnZXBfc2NvcmVzOFtsZV9nZW5lcywgMiwgZHJvcCA9Rl0pCgojIHNjb3JlID0gRmV0Y2hEYXRhKG9iamVjdCA9IGx1bmcsdmFycyA9IGxlX2dlbmVzKSAlPiUgcm93TWVhbnMoKQpwYXRod2F5X25hbWUgPSBwYXN0ZTAoaHlwX29iaiRkYXRhW3BhdGh3YXlfbnVtLCAibGFiZWwiLCBkcm9wID0gRl0sICJfbGUiKSAlPiUgZ3N1YihwYXR0ZXJuID0gIiAiLHJlcGxhY2VtZW50ID0gIl8iKQpsdW5nID0gQWRkTWV0YURhdGEobHVuZywgc2NvcmUsIGNvbC5uYW1lID0gcGF0aHdheV9uYW1lKQpwcmludF90YWIoRmVhdHVyZVBsb3Qob2JqZWN0ID0gbHVuZyxmZWF0dXJlcyA9IHBhdGh3YXlfbmFtZSksdGl0bGUgPSBwYXRod2F5X25hbWUpCmBgYApgYGB7cn0KcGF0aHdheV9udW0gPSAxCmxlX2dlbmVzID0gaHlwX29iaiRhcy5kYXRhLmZyYW1lKClbcGF0aHdheV9udW0sImxlIl0gJT4lIHN0cnNwbGl0KCIsIikgJT4lIHVubGlzdCgpCnNjb3JlID0gKGx1bmdfZXhwcmVzc2lvblssIGxlX2dlbmVzWyBsZV9nZW5lcyAlaW4lIHRvcF9nZW5lc1sxOjIwMF1dXSAlPiUgYXMubWF0cml4KCkpICAlKiUgYXMubWF0cml4KGdlcF9zY29yZXM4W2xlX2dlbmVzWyBsZV9nZW5lcyAlaW4lIHRvcF9nZW5lc1sxOjIwMF1dLCAyLCBkcm9wID1GXSkKY29yKHNjb3JlLHVzYWdlX2J5X2NhbGNbLDJdKQpzY29yZSA9IEZldGNoRGF0YShvYmplY3QgPSBsdW5nLHZhcnMgPSBsZV9nZW5lc1sgbGVfZ2VuZXMgJWluJSB0b3BfZ2VuZXNbMToyMDBdXSkgJT4lIHJvd01lYW5zKCkKY29yKHNjb3JlLHVzYWdlX2J5X2NhbGNbLDJdKQoKICAgICAgICAKdG9wX2dlbmVzID0gZ2VwX3Njb3JlczhbLDJdICU+JSBzZXROYW1lcyhyb3duYW1lcyhnZXBfc2NvcmVzOCkpICU+JSBzb3J0KGRlY3JlYXNpbmcgPSBUUlVFKSAlPiUgbmFtZXMoKQpzY29yZSA9IChsdW5nX2V4cHJlc3Npb25bLCB0b3BfZ2VuZXNbMToyMDBdXSAlPiUgYXMubWF0cml4KCkpICAlKiUgYXMubWF0cml4KGdlcF9zY29yZXM4W3RvcF9nZW5lc1sxOjIwMF0sIDIsIGRyb3AgPUZdKQogdG9wX2dlbmVzICVpbiUgIGxlX2dlbmVzICU+JSB3aGljaCgpCmNvcihzY29yZSx1c2FnZV9ieV9jYWxjWywyXSkKCnBhdGh3YXlfbmFtZSA9IHBhc3RlMChoeXBfb2JqJGRhdGFbcGF0aHdheV9udW0sICJsYWJlbCIsIGRyb3AgPSBGXSwgIl9sZSIpICU+JSBnc3ViKHBhdHRlcm4gPSAiICIscmVwbGFjZW1lbnQgPSAiXyIpCmx1bmcgPSBBZGRNZXRhRGF0YShsdW5nLCBzY29yZSwgY29sLm5hbWUgPSBwYXRod2F5X25hbWUpCnByaW50X3RhYihGZWF0dXJlUGxvdChvYmplY3QgPSBsdW5nLGZlYXR1cmVzID0gcGF0aHdheV9uYW1lKSx0aXRsZSA9IHBhdGh3YXlfbmFtZSkKYGBgCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KbWV0YWdlbmVzX21lYW5fY29tcGFyZShkYXRhc2V0ID0gbHVuZyx0aW1lLnBvaW50X3ZhciA9ICJ0aW1lLnBvaW50IixwcmVmaXggPSAicGF0aWVudCIscGF0aWVudC5pZGVudF92YXIgPSAicGF0aWVudC5pZGVudCIscHJlX29uID0gYygicHJlLXRyZWF0bWVuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdCIsIm9uLXRyZWF0bWVudCIpLHRlc3QgPSAid2lsY294LnRlc3QiLHByb2dyYW1zID0gcGF0aHdheV9uYW1lLHdpdGhvdXRfc3BsaXQgPSBGKQpgYGAKCgojIHByb2dyYW1zIExFIGdlbmVzIHsudGFic2V0fQoKCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KCnByb2dyYW1zX21haW5fcGF0aHdheXNfbmFtZXMgPSBsaXN0KCkKICBmb3IgKGNvbCBpbiBzZXFfYWxvbmcoZ2VwX3Njb3JlczgpWzE6M10pIHsKICAgICByYW5rZWRfdmVjID0gZ2VwX3Njb3JlczhbLGNvbF0gJT4lIHNldE5hbWVzKHJvd25hbWVzKGdlcF9zY29yZXM4KSkgJT4lIHNvcnQoZGVjcmVhc2luZyA9IFRSVUUpIAogICAgIGh5cF9vYmogPC0gaHlwZVJfZmdzZWEocmFua2VkX3ZlYywgZ2VuZXNldHMpCiAgICAgcHJvZ3JhbXNfbWFpbl9wYXRod2F5c19uYW1lc1tbY29sXV0gPSAgaHlwX29iaiRkYXRhW3Byb2dyYW1zX21haW5fcGF0aHdheXNbW2NvbF1dLCJsYWJlbCIsZHJvcD1UXQogICAgIGZvciAocGF0aHdheV9udW0gaW4gcHJvZ3JhbXNfbWFpbl9wYXRod2F5c1tbY29sXV0pIHsKICAgICAgICBsZV9nZW5lcyA9ICBoeXBfb2JqJGRhdGFbcGF0aHdheV9udW0sLGRyb3A9Rl0gJT4lIHB1bGwoImxlIikgJT4lIHN0cnNwbGl0KCIsIikgJT4lIHVubGlzdCgpCiAgICAgICAgIyBzY29yZSA9IChsdW5nX2V4cHJlc3Npb25bLGxlX2dlbmVzXSAlPiUgYXMubWF0cml4KCkpICAlKiUgYXMubWF0cml4KGdlcF9zY29yZXM4W2xlX2dlbmVzLGNvbCxkcm9wPUZdKQogICAgICAgICMgc2NvcmUgPSBzY29yZSAlPiUgYXMudmVjdG9yKCkKICAgICAgICBzY29yZSA9IEZldGNoRGF0YShvYmplY3QgPSBsdW5nLHZhcnMgPSBsZV9nZW5lcykgJT4lIHJvd01lYW5zKCkKICAgICAgICBwYXRod2F5X25hbWUgPSBwYXN0ZTAoaHlwX29iaiRkYXRhW3BhdGh3YXlfbnVtLCJsYWJlbCIsZHJvcD1GXSwiX2xlIikKICAgICAgICBsdW5nPUFkZE1ldGFEYXRhKGx1bmcsc2NvcmUsY29sLm5hbWUgPSBwYXRod2F5X25hbWUpCiAgICAgICAgY29yX3JlcyA9IGNvcihzY29yZSx1c2FnZV9ieV9jYWxjWyxjb2xdKQogICAgICAgIHByaW50X3RhYihGZWF0dXJlUGxvdChvYmplY3QgPSBsdW5nLGZlYXR1cmVzID0gcGF0aHdheV9uYW1lKStnZ3RpdGxlKHBhdGh3YXlfbmFtZSwgc3VidGl0bGUgPSBwYXN0ZSgiY29yIHRvIHVzYWdlOiIsY29yX3JlcykpLHRpdGxlID0gcGF0aHdheV9uYW1lKQogICAgIH0KICB9CmBgYAojIHByb2dyYW1zIExFIGdlbmVzIHsudGFic2V0fQoKYGBge3IgcmVzdWx0cz0nYXNpcyd9Cm1ldGFnZW5lc19tZWFuX2NvbXBhcmUoZGF0YXNldCA9IGx1bmcsdGltZS5wb2ludF92YXIgPSAidGltZS5wb2ludCIscHJlZml4ID0gInBhdGllbnQiLHBhdGllbnQuaWRlbnRfdmFyID0gInBhdGllbnQuaWRlbnQiLHByZV9vbiA9IGMoInByZS10cmVhdG1lbnQiLCJvbi10cmVhdG1lbnQiKSx0ZXN0ID0gIndpbGNveC50ZXN0Iixwcm9ncmFtcyA9IHByb2dyYW1zX21haW5fcGF0aHdheXNfbmFtZXMgJT4lIHVubGlzdCgpICU+JSBwYXN0ZTAoIl9sZSIpICx3aXRob3V0X3NwbGl0ID0gRikKYGBgCg==